You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Godot 4.3自定义可缩放画布滚动位置同步问题求助

Godot 4.3 画布缩放与滚动条同步问题

约一周前,我尝试在Godot 4.3中实现类似Photoshop、Krita的功能:通过鼠标滚轮围绕光标位置缩放作为画布的TextureRect。目前已实现画布缩放及位置调整,滚动条尺寸也可适配,但始终无法让滚动条位置与画布缩放状态成比例同步。

初始代码

extends Control

@onready var canvas: TextureRect = $Canvas
@onready var h_scroll_bar: HScrollBar = $HScrollBar
@onready var v_scroll_bar: VScrollBar = $VScrollBar

var min_zoom = 1.0
var max_zoom = 15.0
var zoom_step = 0.1
var zoom = 1.0
var original_canvas_width: int
var original_canvas_height: int

func _ready():
    original_canvas_width = canvas.size.x
    original_canvas_height = canvas.size.y
    # Initialize the scrollbars when the scene starts
    update_scrollbars()

func _input(event):
    # Handle zooming with mouse wheel
    if event is InputEventMouseButton:
        if event.button_index == MOUSE_BUTTON_WHEEL_UP:
            zoom_canvas(event.global_position, zoom_step)
        elif event.button_index == MOUSE_BUTTON_WHEEL_DOWN:
            zoom_canvas(event.global_position, -zoom_step)

func zoom_canvas(mouse_position: Vector2, zoom_delta: float):
    var new_zoom = clamp(zoom + zoom_delta, min_zoom, max_zoom)
    ifif new_zoom == zoom:
        return

    var scale_factor = new_zoom / zoom
    zoom = new_zoom

    # Calculate the new scale
    var old_size = canvas.size
    var new_size = old_size * scale_factor

    # Adjust the position so that zooming centers around the mouse
    var offset = mouse_position - canvas.position
    var zoom_offset = offset * (1 - scale_factor)
    canvas.size = new_size
    canvas.position += zoom_offset

    # Update scrollbars
    update_scrollbars()

func update_scrollbars():
    var canvas_size_x_ratio: float = (original_canvas_width/ size.x)
    var canvas_size_y_ratio: float = (original_canvas_height/ size.y)
    h_scroll_bar.page = ( canvas_size_x_ratio / zoom )  * h_scroll_bar.max_value
    v_scroll_bar.page = ( canvas_size_y_ratio / zoom )  * v_scroll_bar.max_value
    var x = 0.5
    var y = 0.5
    h_scroll_bar.value = (h_scroll_bar.max_value-h_scroll_bar.page) - (x * (h_scroll_bar.max_value-h_scroll_bar.page))
    v_scroll_bar.value = (v_scroll_bar.max_value-v_scroll_bar.page) - (y * (v_scroll_bar.max_value-v_scroll_bar.page))

我认为滚动条value的计算公式逻辑正确,但需找到合适的x、y相对值使滚动条位置与缩放位置匹配。

更新:新增代码元素

extends Control

@onready var canvas: TextureRect = $Canvas
@onready var h_scroll_bar: HScrollBar = $HScrollBar
@onready var v_scroll_bar: VScrollBar = $VScrollBar

var min_zoom = 1.0
var max_zoom = 15.0
var zoom_step = 0.1
var zoom = 1.0
var original_canvas_width: int
var original_canvas_height: int
var virtual_space_h: int
var virtual_space_v: int
var virtual_space_pos: Vector2
var last_mouse_pos: Vector2 = Vector2.ZERO  # Store the last mouse position
#var last_mouse_pos: Vector2 = Vector2(size.x/2, size.y/2)  # Store the last mouse position

func _ready():
    original_canvas_width = canvas.size.x
    original_canvas_height = canvas.size.y
    
    virtual_space_h = size.x
    virtual_space_v = size.y
    virtual_space_pos = Vector2(0,0)
    update_scrollbars()

func _input(event):
    if event is InputEventMouseButton:
        if event.button_index == MOUSE_BUTTON_WHEEL_UP:
            zoom_canvas(event.global_position, zoom_step)
        elif event.button_index == MOUSE_BUTTON_WHEEL_DOWN:
            zoom_canvas(event.global_position, -zoom_step)

func zoom_canvas(mouse_position: Vector2, zoom_delta: float):
    var new_zoom = clamp(zoom + zoom_delta, min_zoom, max_zoom)
    if new_zoom == zoom:
        return

    var scale_factor = new_zoom / zoom
    zoom = new_zoom

    # Get mouse position relative to canvas before scaling
    var mouse_offset = mouse_position - canvas.position
    var old_size = canvas.size
    var old_space = size
    var new_size = old_size * scale_factor
    var new_space = old_space * scale_factor

    # Adjust the position based on zoom around the mouse cursor
    var zoom_offset = mouse_offset * (1 - scale_factor)

    canvas.size = new_size
    canvas.position += zoom_offset

    virtual_space_h = new_space.x
    virtual_space_v = new_space.y
    virtual_space_pos += zoom_offset
    # Update the scrollbars to match the new canvas size and zoom
    update_scrollbars()

    # Store the last mouse position (relative to the canvas)
    last_mouse_pos = mouse_position

func update_scrollbars():
    # Calculate the ratio of the canvas size relative to the control size
    var canvas_size_x_ratio: float = (original_canvas_width / size.x)
    var canvas_size_y_ratio: float = (original_canvas_height / size.y)
    
    # Update the scrollbar page values based on zoom level
    h_scroll_bar.page = (canvas_size_x_ratio / zoom) * h_scroll_bar.max_value
    v_scroll_bar.page = (canvas_size_y_ratio / zoom) * v_scroll_bar.max_value
    
    # Calculate the relative position adjustments based on the last mouse position
    #var x = (last_mouse_pos.x - canvas.position.x) / canvas.size.x
    #var y = (last_mouse_pos.y - canvas.position.y) / canvas.size.y

    var x = (last_mouse_pos.x - virtual_space_pos.x) / virtual_space_h
    var y = (last_mouse_pos.y - virtual_space_pos.y) / virtual_space_v
    # Update the scrollbar values based on mouse position relative to canvas
    h_scroll_bar.value = (h_scroll_bar.max_value - h_scroll_bar.page) - (x * (h_scroll_bar.max_value - h_scroll_bar.page))
    v_scroll_bar.value = (v_scroll_bar.max_value - v_scroll_bar.page) - (y * (v_scroll_bar.max_value - v_scroll_bar.page))

目前围绕鼠标位置缩放画布的功能流畅可用,但滚动条仍无法与画布状态良好同步,寻求解决方法。


内容的提问来源于stack exchange,提问作者New developer

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.15 15:36:07