extends Node ## UI动画管理器 ## 统一管理UI动画效果,提升用户体验 # 动画配置 const FADE_DURATION = 0.25 const SLIDE_DURATION = 0.3 const SCALE_DURATION = 0.2 const BOUNCE_DURATION = 0.4 # 缓动类型 enum EaseType { EASE_IN, EASE_OUT, EASE_IN_OUT, BOUNCE, ELASTIC } ## 淡入动画 func fade_in(node: Control, duration: float = FADE_DURATION, ease_type: EaseType = EaseType.EASE_OUT) -> Tween: """ 淡入动画 @param node: 要动画的节点 @param duration: 动画时长 @param ease_type: 缓动类型 @return: Tween对象 """ if not node: return null node.modulate.a = 0.0 node.show() var tween = node.create_tween() tween.set_ease(_get_tween_ease(ease_type)) tween.tween_property(node, "modulate:a", 1.0, duration) return tween ## 淡出动画 func fade_out(node: Control, duration: float = FADE_DURATION, ease_type: EaseType = EaseType.EASE_IN) -> Tween: """ 淡出动画 @param node: 要动画的节点 @param duration: 动画时长 @param ease_type: 缓动类型 @return: Tween对象 """ if not node: return null var tween = node.create_tween() tween.set_ease(_get_tween_ease(ease_type)) tween.tween_property(node, "modulate:a", 0.0, duration) tween.tween_callback(func(): node.hide()) return tween ## 滑入动画(从左侧) func slide_in_left(node: Control, duration: float = SLIDE_DURATION, ease_type: EaseType = EaseType.EASE_OUT) -> Tween: """ 从左侧滑入动画 @param node: 要动画的节点 @param duration: 动画时长 @param ease_type: 缓动类型 @return: Tween对象 """ if not node: return null var original_pos = node.position node.position.x = -node.size.x node.show() var tween = node.create_tween() tween.set_ease(_get_tween_ease(ease_type)) tween.tween_property(node, "position", original_pos, duration) return tween ## 滑入动画(从右侧) func slide_in_right(node: Control, duration: float = SLIDE_DURATION, ease_type: EaseType = EaseType.EASE_OUT) -> Tween: """ 从右侧滑入动画 @param node: 要动画的节点 @param duration: 动画时长 @param ease_type: 缓动类型 @return: Tween对象 """ if not node: return null var original_pos = node.position var viewport_width = node.get_viewport().get_visible_rect().size.x node.position.x = viewport_width node.show() var tween = node.create_tween() tween.set_ease(_get_tween_ease(ease_type)) tween.tween_property(node, "position", original_pos, duration) return tween ## 滑入动画(从上方) func slide_in_top(node: Control, duration: float = SLIDE_DURATION, ease_type: EaseType = EaseType.EASE_OUT) -> Tween: """ 从上方滑入动画 @param node: 要动画的节点 @param duration: 动画时长 @param ease_type: 缓动类型 @return: Tween对象 """ if not node: return null var original_pos = node.position node.position.y = -node.size.y node.show() var tween = node.create_tween() tween.set_ease(_get_tween_ease(ease_type)) tween.tween_property(node, "position", original_pos, duration) return tween ## 滑入动画(从下方) func slide_in_bottom(node: Control, duration: float = SLIDE_DURATION, ease_type: EaseType = EaseType.EASE_OUT) -> Tween: """ 从下方滑入动画 @param node: 要动画的节点 @param duration: 动画时长 @param ease_type: 缓动类型 @return: Tween对象 """ if not node: return null var original_pos = node.position var viewport_height = node.get_viewport().get_visible_rect().size.y node.position.y = viewport_height node.show() var tween = node.create_tween() tween.set_ease(_get_tween_ease(ease_type)) tween.tween_property(node, "position", original_pos, duration) return tween ## 缩放动画(弹出效果) func scale_popup(node: Control, duration: float = SCALE_DURATION, ease_type: EaseType = EaseType.BOUNCE) -> Tween: """ 缩放弹出动画 @param node: 要动画的节点 @param duration: 动画时长 @param ease_type: 缓动类型 @return: Tween对象 """ if not node: return null node.scale = Vector2.ZERO node.show() var tween = node.create_tween() tween.set_ease(_get_tween_ease(ease_type)) tween.tween_property(node, "scale", Vector2.ONE, duration) return tween ## 按钮点击反馈动画 func button_press_feedback(button: Button, scale_factor: float = 0.95, duration: float = 0.1) -> Tween: """ 按钮点击反馈动画 @param button: 按钮节点 @param scale_factor: 缩放因子 @param duration: 动画时长 @return: Tween对象 """ if not button: return null var tween = button.create_tween() tween.tween_property(button, "scale", Vector2.ONE * scale_factor, duration) tween.tween_property(button, "scale", Vector2.ONE, duration) return tween ## 摇摆动画(错误提示) func shake_error(node: Control, intensity: float = 10.0, duration: float = 0.5) -> Tween: """ 摇摆动画(用于错误提示) @param node: 要动画的节点 @param intensity: 摇摆强度 @param duration: 动画时长 @return: Tween对象 """ if not node: return null var original_pos = node.position var tween = node.create_tween() # 创建摇摆效果 for i in range(4): var offset = Vector2(randf_range(-intensity, intensity), 0) tween.tween_property(node, "position", original_pos + offset, duration / 8) tween.tween_property(node, "position", original_pos, duration / 8) return tween ## 脉冲动画(强调效果) func pulse_highlight(node: Control, scale_max: float = 1.1, duration: float = 0.6) -> Tween: """ 脉冲动画(强调效果) @param node: 要动画的节点 @param scale_max: 最大缩放 @param duration: 动画时长 @return: Tween对象 """ if not node: return null var tween = node.create_tween() tween.set_loops() # 无限循环 tween.tween_property(node, "scale", Vector2.ONE * scale_max, duration / 2) tween.tween_property(node, "scale", Vector2.ONE, duration / 2) return tween ## 停止脉冲动画 func stop_pulse(node: Control) -> void: """ 停止脉冲动画并恢复原始缩放 @param node: 节点 """ if not node: return # 停止所有tween node.get_tree().call_group("tween", "kill") # 恢复原始缩放 var tween = node.create_tween() tween.tween_property(node, "scale", Vector2.ONE, 0.2) ## 组合动画:淡入+滑入 func fade_slide_in(node: Control, direction: String = "bottom", duration: float = SLIDE_DURATION) -> Tween: """ 组合动画:淡入+滑入 @param node: 要动画的节点 @param direction: 滑入方向("left", "right", "top", "bottom") @param duration: 动画时长 @return: Tween对象 """ if not node: return null # 设置初始状态 node.modulate.a = 0.0 var original_pos = node.position match direction: "left": node.position.x = -node.size.x "right": node.position.x = node.get_viewport().get_visible_rect().size.x "top": node.position.y = -node.size.y "bottom": node.position.y = node.get_viewport().get_visible_rect().size.y node.show() # 创建并行动画 var tween = node.create_tween() tween.set_parallel(true) tween.tween_property(node, "modulate:a", 1.0, duration) tween.tween_property(node, "position", original_pos, duration) return tween ## 获取Tween缓动类型 func _get_tween_ease(ease_type: EaseType) -> Tween.EaseType: """ 将自定义缓动类型转换为Tween缓动类型 @param ease_type: 自定义缓动类型 @return: Tween缓动类型 """ match ease_type: EaseType.EASE_IN: return Tween.EASE_IN EaseType.EASE_OUT: return Tween.EASE_OUT EaseType.EASE_IN_OUT: return Tween.EASE_IN_OUT EaseType.BOUNCE: return Tween.EASE_OUT # Godot没有内置bounce,使用ease_out EaseType.ELASTIC: return Tween.EASE_OUT # Godot没有内置elastic,使用ease_out _: return Tween.EASE_OUT ## 创建加载动画 func create_loading_spinner(parent: Control, size: Vector2 = Vector2(32, 32)) -> Control: """ 创建加载旋转动画 @param parent: 父节点 @param size: 大小 @return: 旋转节点 """ var spinner = ColorRect.new() spinner.size = size spinner.color = Color.WHITE # 创建简单的旋转图形 var style = StyleBoxFlat.new() style.bg_color = Color.TRANSPARENT style.border_width_top = 3 style.border_width_right = 1 style.border_width_bottom = 1 style.border_width_left = 1 style.border_color = Color.WHITE style.corner_radius_top_left = size.x / 2 style.corner_radius_top_right = size.x / 2 style.corner_radius_bottom_left = size.x / 2 style.corner_radius_bottom_right = size.x / 2 spinner.add_theme_stylebox_override("panel", style) parent.add_child(spinner) # 添加旋转动画 var tween = spinner.create_tween() tween.set_loops() tween.tween_property(spinner, "rotation", TAU, 1.0) return spinner ## 移除加载动画 func remove_loading_spinner(spinner: Control) -> void: """ 移除加载旋转动画 @param spinner: 旋转节点 """ if spinner and is_instance_valid(spinner): spinner.queue_free()