Godot 4中标签栏滚动容器跟随选中标签的节点裁剪逻辑问题
Godot 4 标签栏滚动容器跟随选中标签的优化方案
问题背景
我在Godot 4中尝试实现标签栏的滚动容器跟随选中标签按钮的功能,初始代码如下:
# A helper for making a scroll container in a tab bar # follow the selected tab button. class_name PS3TabBarScrollFollow static func apply(tab_bar: PS3TabBar, container: ScrollContainer) -> void: tab_bar.tab_changed.connect(func(_tab): var btn = tab_bar.current_tab_button container.scroll_horizontal = (btn.global_position.x - container.global_position.x))
这段代码能实现基础的跟随功能,但用鼠标滚轮操作滚动条后,切换标签时滚动位置会突然跳转。由于自定义了标签栏按钮且未使用焦点,follow_focus属性无法生效。需要添加按钮是否被容器裁剪的判断逻辑,仅在按钮不可见时才调整滚动位置。
解决方案:裁剪判断与滚动逻辑优化
核心思路是先判断选中的标签按钮是否完全处于滚动容器的可视范围内,仅当按钮部分或完全不可见时,才调整滚动位置让按钮显示在合适区域。
1. 实现裁剪判断辅助函数
先编写一个静态函数,用于判断按钮是否被滚动容器裁剪:
# 判断节点是否被容器裁剪(部分或完全不可见) static func is_clipped_by(node: Control, container: ScrollContainer) -> bool: # 将按钮的全局矩形转换为容器的局部坐标系 var node_local_rect = node.get_global_rect().transform(container.get_global_transform().affine_inverse()) # 计算容器的可视区域(包含当前滚动偏移) var container_visible_rect = Rect2( container.scroll_horizontal, container.scroll_vertical, container.get_viewport_rect().size.x, container.get_viewport_rect().size.y ) # 检查按钮矩形是否完全被可视区域包含 return not container_visible_rect.encloses(node_local_rect)
2. 优化滚动跟随逻辑
修改原apply函数,加入裁剪判断,仅在必要时调整滚动位置,同时优化滚动目标让按钮居中显示:
class_name PS3TabBarScrollFollow static func apply(tab_bar: PS3TabBar, container: ScrollContainer) -> void: tab_bar.tab_changed.connect(func(_tab): var btn = tab_bar.current_tab_button # 仅当按钮被裁剪时才调整滚动位置 if is_clipped_by(btn, container): # 计算让按钮居中的目标滚动值 var container_visible_width = container.get_viewport_rect().size.x var btn_center_global = btn.global_position.x + btn.size.x / 2 var target_scroll = btn_center_global - container.global_position.x - container_visible_width / 2 # 限制滚动范围,避免超出滚动条边界 target_scroll = clamp(target_scroll, 0, container.get_h_scroll_bar().max_value) container.scroll_horizontal = target_scroll ) ) # 辅助函数:判断节点是否被容器裁剪 static func is_clipped_by(node: Control, container: ScrollContainer) -> bool: var node_local_rect = node.get_global_rect().transform(container.get_global_transform().affine_inverse()) var container_visible_rect = Rect2( container.scroll_horizontal, container.scroll_vertical, container.get_viewport_rect().size.x, container.get_viewport_rect().size.y ) return not container_visible_rect.encloses(node_local_rect)
逻辑说明
- 坐标转换:通过全局坐标系到容器局部坐标系的转换,准确对比按钮位置和容器可视区域。
- 可视区域计算:容器的可视区域需要结合当前滚动偏移和自身视口尺寸,确保判断准确。
- 居中滚动:让选中按钮的中心对齐容器可视区域的中心,提升交互流畅度;使用
clamp函数限制滚动值,避免滚动条超出有效范围。 - 按需滚动:只有当按钮不在可视区域内时才触发滚动调整,彻底解决手动滚动后切换标签的突兀跳转问题。
内容的提问来源于stack exchange,提问作者Hydroper
相关产品推荐
相关产品推荐

