2025-07-25 15:18:05 +08:00
|
|
|
|
import sys
|
2025-07-18 12:01:51 +08:00
|
|
|
|
from PySide6.QtCore import (QObject, QPropertyAnimation, QParallelAnimationGroup,
|
2025-07-25 15:18:05 +08:00
|
|
|
|
QPoint, QEasingCurve, QTimer, Signal, QRect)
|
|
|
|
|
|
from PySide6.QtWidgets import QGraphicsOpacityEffect, QPushButton
|
|
|
|
|
|
from PySide6.QtGui import QColor
|
2025-07-15 16:30:50 +08:00
|
|
|
|
|
2025-07-18 12:01:51 +08:00
|
|
|
|
class MultiStageAnimations(QObject):
|
|
|
|
|
|
animation_finished = Signal()
|
|
|
|
|
|
def __init__(self, ui, parent=None):
|
|
|
|
|
|
super().__init__(parent)
|
2025-07-15 16:30:50 +08:00
|
|
|
|
self.ui = ui
|
2025-07-25 15:18:05 +08:00
|
|
|
|
self.parent = parent # 保存父窗口引用以获取当前尺寸
|
|
|
|
|
|
|
|
|
|
|
|
# 获取画布尺寸 - 动态从父窗口获取
|
|
|
|
|
|
if parent:
|
|
|
|
|
|
self.canvas_width = parent.width()
|
|
|
|
|
|
self.canvas_height = parent.height()
|
|
|
|
|
|
else:
|
|
|
|
|
|
# 默认尺寸
|
|
|
|
|
|
self.canvas_width = 1280
|
|
|
|
|
|
self.canvas_height = 720
|
2025-07-15 16:30:50 +08:00
|
|
|
|
|
|
|
|
|
|
# 动画时序配置
|
|
|
|
|
|
self.animation_config = {
|
|
|
|
|
|
"logo": {
|
|
|
|
|
|
"delay_after": 2800
|
|
|
|
|
|
},
|
|
|
|
|
|
"mainbg": {
|
|
|
|
|
|
"delay_after": 500
|
2025-07-25 15:18:05 +08:00
|
|
|
|
},
|
|
|
|
|
|
"button_click": {
|
|
|
|
|
|
"scale_duration": 100,
|
|
|
|
|
|
"scale_min": 0.95,
|
|
|
|
|
|
"scale_max": 1.0
|
2025-07-15 16:30:50 +08:00
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2025-07-25 15:18:05 +08:00
|
|
|
|
# 第一阶段:Logo动画配置,根据新布局调整Y坐标
|
2025-07-15 16:30:50 +08:00
|
|
|
|
self.logo_widgets = [
|
2025-07-25 15:18:05 +08:00
|
|
|
|
{"widget": ui.vol1bg, "delay": 0, "duration": 500, "end_pos": QPoint(0, 150)},
|
|
|
|
|
|
{"widget": ui.vol2bg, "delay": 80, "duration": 500, "end_pos": QPoint(0, 210)},
|
|
|
|
|
|
{"widget": ui.vol3bg, "delay": 160, "duration": 500, "end_pos": QPoint(0, 270)},
|
|
|
|
|
|
{"widget": ui.vol4bg, "delay": 240, "duration": 500, "end_pos": QPoint(0, 330)},
|
|
|
|
|
|
{"widget": ui.afterbg, "delay": 320, "duration": 500, "end_pos": QPoint(0, 390)}
|
2025-07-15 16:30:50 +08:00
|
|
|
|
]
|
|
|
|
|
|
|
2025-07-25 15:18:05 +08:00
|
|
|
|
# 第二阶段:菜单元素,位置会在开始动画时动态计算
|
2025-07-15 16:30:50 +08:00
|
|
|
|
self.menu_widgets = [
|
2025-07-25 15:18:05 +08:00
|
|
|
|
# 移除菜单背景动画
|
|
|
|
|
|
# {"widget": ui.menubg, "end_pos": QPoint(720, 55), "duration": 600},
|
|
|
|
|
|
{"widget": ui.button_container, "end_pos": None, "duration": 600},
|
2025-07-25 17:00:55 +08:00
|
|
|
|
{"widget": ui.uninstall_container, "end_pos": None, "duration": 600}, # 添加卸载补丁按钮
|
2025-07-25 15:18:05 +08:00
|
|
|
|
{"widget": ui.exit_container, "end_pos": None, "duration": 600}
|
2025-07-15 16:30:50 +08:00
|
|
|
|
]
|
|
|
|
|
|
|
|
|
|
|
|
self.animations = []
|
|
|
|
|
|
self.timers = []
|
2025-07-25 15:18:05 +08:00
|
|
|
|
|
|
|
|
|
|
# 设置按钮点击动画
|
|
|
|
|
|
self.setup_button_click_animations()
|
|
|
|
|
|
|
|
|
|
|
|
def setup_button_click_animations(self):
|
|
|
|
|
|
"""设置按钮点击动画"""
|
|
|
|
|
|
# 为开始安装按钮添加点击动画
|
|
|
|
|
|
self.ui.start_install_btn.pressed.connect(
|
|
|
|
|
|
lambda: self.start_button_click_animation(self.ui.button_container)
|
|
|
|
|
|
)
|
|
|
|
|
|
self.ui.start_install_btn.released.connect(
|
|
|
|
|
|
lambda: self.end_button_click_animation(self.ui.button_container)
|
|
|
|
|
|
)
|
|
|
|
|
|
|
2025-07-25 17:00:55 +08:00
|
|
|
|
# 为卸载补丁按钮添加点击动画
|
|
|
|
|
|
self.ui.uninstall_btn.pressed.connect(
|
|
|
|
|
|
lambda: self.start_button_click_animation(self.ui.uninstall_container)
|
|
|
|
|
|
)
|
|
|
|
|
|
self.ui.uninstall_btn.released.connect(
|
|
|
|
|
|
lambda: self.end_button_click_animation(self.ui.uninstall_container)
|
|
|
|
|
|
)
|
|
|
|
|
|
|
2025-07-25 15:18:05 +08:00
|
|
|
|
# 为退出按钮添加点击动画
|
|
|
|
|
|
self.ui.exit_btn.pressed.connect(
|
|
|
|
|
|
lambda: self.start_button_click_animation(self.ui.exit_container)
|
|
|
|
|
|
)
|
|
|
|
|
|
self.ui.exit_btn.released.connect(
|
|
|
|
|
|
lambda: self.end_button_click_animation(self.ui.exit_container)
|
|
|
|
|
|
)
|
|
|
|
|
|
|
|
|
|
|
|
def start_button_click_animation(self, button_container):
|
|
|
|
|
|
"""开始按钮点击动画"""
|
|
|
|
|
|
# 创建缩放动画
|
|
|
|
|
|
scale_anim = QPropertyAnimation(button_container.children()[0], b"geometry") # 只对按钮背景应用动画
|
|
|
|
|
|
scale_anim.setDuration(self.animation_config["button_click"]["scale_duration"])
|
|
|
|
|
|
|
|
|
|
|
|
# 获取当前几何形状
|
|
|
|
|
|
current_geometry = button_container.children()[0].geometry()
|
|
|
|
|
|
|
|
|
|
|
|
# 计算缩放后的几何形状(保持中心点不变)
|
|
|
|
|
|
scale_factor = self.animation_config["button_click"]["scale_min"]
|
|
|
|
|
|
width_diff = current_geometry.width() * (1 - scale_factor) / 2
|
|
|
|
|
|
height_diff = current_geometry.height() * (1 - scale_factor) / 2
|
|
|
|
|
|
|
|
|
|
|
|
new_geometry = QRect(
|
|
|
|
|
|
current_geometry.x() + width_diff,
|
|
|
|
|
|
current_geometry.y() + height_diff,
|
|
|
|
|
|
current_geometry.width() * scale_factor,
|
|
|
|
|
|
current_geometry.height() * scale_factor
|
|
|
|
|
|
)
|
|
|
|
|
|
|
|
|
|
|
|
scale_anim.setEndValue(new_geometry)
|
|
|
|
|
|
scale_anim.setEasingCurve(QEasingCurve.Type.OutQuad)
|
|
|
|
|
|
|
|
|
|
|
|
# 启动动画
|
|
|
|
|
|
scale_anim.start()
|
|
|
|
|
|
self.animations.append(scale_anim)
|
|
|
|
|
|
|
|
|
|
|
|
# 对文本标签也应用同样的动画
|
|
|
|
|
|
text_anim = QPropertyAnimation(button_container.children()[1], b"geometry")
|
|
|
|
|
|
text_anim.setDuration(self.animation_config["button_click"]["scale_duration"])
|
|
|
|
|
|
text_geometry = button_container.children()[1].geometry()
|
|
|
|
|
|
|
|
|
|
|
|
new_text_geometry = QRect(
|
|
|
|
|
|
text_geometry.x() + width_diff,
|
|
|
|
|
|
text_geometry.y() + height_diff,
|
|
|
|
|
|
text_geometry.width() * scale_factor,
|
|
|
|
|
|
text_geometry.height() * scale_factor
|
|
|
|
|
|
)
|
|
|
|
|
|
|
|
|
|
|
|
text_anim.setEndValue(new_text_geometry)
|
|
|
|
|
|
text_anim.setEasingCurve(QEasingCurve.Type.OutQuad)
|
|
|
|
|
|
text_anim.start()
|
|
|
|
|
|
self.animations.append(text_anim)
|
|
|
|
|
|
|
|
|
|
|
|
def end_button_click_animation(self, button_container):
|
|
|
|
|
|
"""结束按钮点击动画,恢复正常外观"""
|
|
|
|
|
|
# 创建恢复动画 - 对背景
|
|
|
|
|
|
scale_anim = QPropertyAnimation(button_container.children()[0], b"geometry")
|
|
|
|
|
|
scale_anim.setDuration(self.animation_config["button_click"]["scale_duration"])
|
|
|
|
|
|
|
|
|
|
|
|
# 恢复到原始大小 (10,10,191,91)
|
|
|
|
|
|
original_geometry = QRect(10, 10, 191, 91)
|
|
|
|
|
|
scale_anim.setEndValue(original_geometry)
|
|
|
|
|
|
scale_anim.setEasingCurve(QEasingCurve.Type.OutElastic)
|
|
|
|
|
|
|
|
|
|
|
|
# 启动动画
|
|
|
|
|
|
scale_anim.start()
|
|
|
|
|
|
self.animations.append(scale_anim)
|
|
|
|
|
|
|
|
|
|
|
|
# 恢复文本标签
|
|
|
|
|
|
text_anim = QPropertyAnimation(button_container.children()[1], b"geometry")
|
|
|
|
|
|
text_anim.setDuration(self.animation_config["button_click"]["scale_duration"])
|
|
|
|
|
|
|
|
|
|
|
|
# 恢复文本到原始大小 (10,7,191,91)
|
|
|
|
|
|
text_anim.setEndValue(QRect(10, 7, 191, 91))
|
|
|
|
|
|
text_anim.setEasingCurve(QEasingCurve.Type.OutElastic)
|
|
|
|
|
|
text_anim.start()
|
|
|
|
|
|
self.animations.append(text_anim)
|
|
|
|
|
|
|
2025-07-15 16:30:50 +08:00
|
|
|
|
def initialize(self):
|
|
|
|
|
|
"""初始化所有组件状态"""
|
2025-07-25 15:18:05 +08:00
|
|
|
|
# 更新画布尺寸
|
|
|
|
|
|
if self.parent:
|
|
|
|
|
|
self.canvas_width = self.parent.width()
|
|
|
|
|
|
self.canvas_height = self.parent.height()
|
|
|
|
|
|
|
2025-07-15 16:30:50 +08:00
|
|
|
|
# 设置Mainbg初始状态
|
|
|
|
|
|
effect = QGraphicsOpacityEffect(self.ui.Mainbg)
|
|
|
|
|
|
effect.setOpacity(0)
|
|
|
|
|
|
self.ui.Mainbg.setGraphicsEffect(effect)
|
|
|
|
|
|
|
|
|
|
|
|
# 初始化Logo位置(移到左侧外)
|
|
|
|
|
|
for item in self.logo_widgets:
|
|
|
|
|
|
widget = item["widget"]
|
|
|
|
|
|
effect = QGraphicsOpacityEffect(widget)
|
|
|
|
|
|
effect.setOpacity(0)
|
|
|
|
|
|
widget.setGraphicsEffect(effect)
|
|
|
|
|
|
widget.move(-widget.width(), item["end_pos"].y())
|
|
|
|
|
|
widget.show()
|
|
|
|
|
|
print("初始化支持栏动画")
|
|
|
|
|
|
|
|
|
|
|
|
# 初始化菜单元素(底部外)
|
|
|
|
|
|
for item in self.menu_widgets:
|
|
|
|
|
|
widget = item["widget"]
|
|
|
|
|
|
effect = QGraphicsOpacityEffect(widget)
|
|
|
|
|
|
effect.setOpacity(0)
|
|
|
|
|
|
widget.setGraphicsEffect(effect)
|
|
|
|
|
|
widget.move(widget.x(), self.canvas_height + 100)
|
|
|
|
|
|
widget.show()
|
|
|
|
|
|
|
|
|
|
|
|
def start_logo_animations(self):
|
|
|
|
|
|
"""启动Logo动画序列"""
|
|
|
|
|
|
for item in self.logo_widgets:
|
|
|
|
|
|
timer = QTimer()
|
|
|
|
|
|
timer.setSingleShot(True)
|
|
|
|
|
|
timer.timeout.connect(
|
|
|
|
|
|
lambda w=item["widget"], d=item["duration"], pos=item["end_pos"]:
|
|
|
|
|
|
self.animate_logo(w, pos, d)
|
|
|
|
|
|
)
|
|
|
|
|
|
timer.start(item["delay"])
|
|
|
|
|
|
self.timers.append(timer)
|
|
|
|
|
|
|
|
|
|
|
|
def animate_logo(self, widget, end_pos, duration):
|
|
|
|
|
|
"""执行单个Logo动画"""
|
|
|
|
|
|
anim_group = QParallelAnimationGroup()
|
|
|
|
|
|
|
|
|
|
|
|
# 位置动画
|
|
|
|
|
|
pos_anim = QPropertyAnimation(widget, b"pos")
|
|
|
|
|
|
pos_anim.setDuration(duration)
|
|
|
|
|
|
pos_anim.setStartValue(QPoint(-widget.width(), end_pos.y()))
|
|
|
|
|
|
pos_anim.setEndValue(end_pos)
|
2025-07-16 13:01:56 +08:00
|
|
|
|
pos_anim.setEasingCurve(QEasingCurve.Type.OutBack)
|
2025-07-15 16:30:50 +08:00
|
|
|
|
|
|
|
|
|
|
# 透明度动画
|
|
|
|
|
|
opacity_anim = QPropertyAnimation(widget.graphicsEffect(), b"opacity")
|
|
|
|
|
|
opacity_anim.setDuration(duration)
|
|
|
|
|
|
opacity_anim.setStartValue(0)
|
|
|
|
|
|
opacity_anim.setEndValue(1)
|
|
|
|
|
|
|
|
|
|
|
|
anim_group.addAnimation(pos_anim)
|
|
|
|
|
|
anim_group.addAnimation(opacity_anim)
|
|
|
|
|
|
|
|
|
|
|
|
# 最后一个Logo动画完成后添加延迟
|
|
|
|
|
|
if widget == self.logo_widgets[-1]["widget"]:
|
|
|
|
|
|
anim_group.finished.connect(
|
|
|
|
|
|
lambda: QTimer.singleShot(
|
|
|
|
|
|
self.animation_config["logo"]["delay_after"],
|
|
|
|
|
|
self.start_mainbg_animation
|
|
|
|
|
|
)
|
|
|
|
|
|
)
|
|
|
|
|
|
|
|
|
|
|
|
anim_group.start()
|
|
|
|
|
|
self.animations.append(anim_group)
|
|
|
|
|
|
|
|
|
|
|
|
def start_mainbg_animation(self):
|
|
|
|
|
|
"""启动主背景淡入动画(带延迟)"""
|
|
|
|
|
|
main_anim = QPropertyAnimation(self.ui.Mainbg.graphicsEffect(), b"opacity")
|
|
|
|
|
|
main_anim.setDuration(800)
|
|
|
|
|
|
main_anim.setStartValue(0)
|
|
|
|
|
|
main_anim.setEndValue(1)
|
|
|
|
|
|
main_anim.finished.connect(
|
|
|
|
|
|
lambda: QTimer.singleShot(
|
|
|
|
|
|
self.animation_config["mainbg"]["delay_after"],
|
|
|
|
|
|
self.start_menu_animations
|
|
|
|
|
|
)
|
|
|
|
|
|
)
|
|
|
|
|
|
main_anim.start()
|
|
|
|
|
|
self.animations.append(main_anim)
|
|
|
|
|
|
def start_menu_animations(self):
|
|
|
|
|
|
"""启动菜单动画(从下往上)"""
|
2025-07-25 15:18:05 +08:00
|
|
|
|
# 更新按钮最终位置
|
|
|
|
|
|
self._update_button_positions()
|
|
|
|
|
|
|
2025-07-25 17:00:55 +08:00
|
|
|
|
# 跟踪最后一个动画,用于连接finished信号
|
|
|
|
|
|
last_anim = None
|
|
|
|
|
|
|
2025-07-15 16:30:50 +08:00
|
|
|
|
for item in self.menu_widgets:
|
|
|
|
|
|
anim_group = QParallelAnimationGroup()
|
|
|
|
|
|
|
|
|
|
|
|
# 位置动画(从下往上)
|
|
|
|
|
|
pos_anim = QPropertyAnimation(item["widget"], b"pos")
|
|
|
|
|
|
pos_anim.setDuration(item["duration"])
|
|
|
|
|
|
pos_anim.setStartValue(QPoint(item["end_pos"].x(), self.canvas_height + 100))
|
|
|
|
|
|
pos_anim.setEndValue(item["end_pos"])
|
2025-07-16 13:01:56 +08:00
|
|
|
|
pos_anim.setEasingCurve(QEasingCurve.Type.OutBack)
|
2025-07-15 16:30:50 +08:00
|
|
|
|
|
|
|
|
|
|
# 透明度动画
|
|
|
|
|
|
opacity_anim = QPropertyAnimation(item["widget"].graphicsEffect(), b"opacity")
|
|
|
|
|
|
opacity_anim.setDuration(item["duration"])
|
|
|
|
|
|
opacity_anim.setStartValue(0)
|
|
|
|
|
|
opacity_anim.setEndValue(1)
|
|
|
|
|
|
|
|
|
|
|
|
anim_group.addAnimation(pos_anim)
|
|
|
|
|
|
anim_group.addAnimation(opacity_anim)
|
2025-07-18 12:01:51 +08:00
|
|
|
|
|
2025-07-25 17:00:55 +08:00
|
|
|
|
# 记录最后一个按钮的动画
|
2025-07-24 17:43:11 +08:00
|
|
|
|
if item["widget"] == self.ui.exit_container:
|
2025-07-25 17:00:55 +08:00
|
|
|
|
last_anim = anim_group
|
2025-07-18 12:01:51 +08:00
|
|
|
|
|
2025-07-15 16:30:50 +08:00
|
|
|
|
anim_group.start()
|
|
|
|
|
|
self.animations.append(anim_group)
|
2025-07-25 17:00:55 +08:00
|
|
|
|
|
|
|
|
|
|
# 在最后一个动画完成时发出信号
|
|
|
|
|
|
if last_anim:
|
|
|
|
|
|
last_anim.finished.connect(self.animation_finished.emit)
|
2025-07-25 15:18:05 +08:00
|
|
|
|
|
|
|
|
|
|
def _update_button_positions(self):
|
|
|
|
|
|
"""更新按钮最终位置"""
|
|
|
|
|
|
# 根据当前窗口大小动态计算按钮位置
|
|
|
|
|
|
if self.parent:
|
|
|
|
|
|
width = self.parent.width()
|
|
|
|
|
|
height = self.parent.height()
|
|
|
|
|
|
|
|
|
|
|
|
# 计算按钮位置
|
|
|
|
|
|
right_margin = 20 # 减小右边距,使按钮更靠右
|
|
|
|
|
|
|
|
|
|
|
|
# 开始安装按钮
|
|
|
|
|
|
if hasattr(self.ui, 'button_container'):
|
|
|
|
|
|
btn_width = self.ui.button_container.width()
|
|
|
|
|
|
x_pos = width - btn_width - right_margin
|
2025-07-25 17:00:55 +08:00
|
|
|
|
y_pos = int((height - 65) * 0.28) - 10 # 与resizeEvent中保持一致
|
2025-07-25 15:18:05 +08:00
|
|
|
|
|
|
|
|
|
|
# 更新动画目标位置
|
|
|
|
|
|
for item in self.menu_widgets:
|
|
|
|
|
|
if item["widget"] == self.ui.button_container:
|
|
|
|
|
|
item["end_pos"] = QPoint(x_pos, y_pos)
|
2025-07-25 17:00:55 +08:00
|
|
|
|
|
|
|
|
|
|
# 卸载补丁按钮
|
|
|
|
|
|
if hasattr(self.ui, 'uninstall_container'):
|
|
|
|
|
|
btn_width = self.ui.uninstall_container.width()
|
|
|
|
|
|
x_pos = width - btn_width - right_margin
|
|
|
|
|
|
y_pos = int((height - 65) * 0.46) - 10 # 与resizeEvent中保持一致
|
|
|
|
|
|
|
|
|
|
|
|
# 更新动画目标位置
|
|
|
|
|
|
for item in self.menu_widgets:
|
|
|
|
|
|
if item["widget"] == self.ui.uninstall_container:
|
|
|
|
|
|
item["end_pos"] = QPoint(x_pos, y_pos)
|
2025-07-25 15:18:05 +08:00
|
|
|
|
|
|
|
|
|
|
# 退出按钮
|
|
|
|
|
|
if hasattr(self.ui, 'exit_container'):
|
|
|
|
|
|
btn_width = self.ui.exit_container.width()
|
|
|
|
|
|
x_pos = width - btn_width - right_margin
|
2025-07-25 17:00:55 +08:00
|
|
|
|
y_pos = int((height - 65) * 0.64) - 10 # 与resizeEvent中保持一致
|
2025-07-25 15:18:05 +08:00
|
|
|
|
|
|
|
|
|
|
# 更新动画目标位置
|
|
|
|
|
|
for item in self.menu_widgets:
|
|
|
|
|
|
if item["widget"] == self.ui.exit_container:
|
|
|
|
|
|
item["end_pos"] = QPoint(x_pos, y_pos)
|
|
|
|
|
|
else:
|
|
|
|
|
|
# 默认位置
|
|
|
|
|
|
for item in self.menu_widgets:
|
|
|
|
|
|
if item["widget"] == self.ui.button_container:
|
2025-07-25 17:00:55 +08:00
|
|
|
|
item["end_pos"] = QPoint(1050, 200)
|
|
|
|
|
|
elif item["widget"] == self.ui.uninstall_container:
|
|
|
|
|
|
item["end_pos"] = QPoint(1050, 310)
|
2025-07-25 15:18:05 +08:00
|
|
|
|
elif item["widget"] == self.ui.exit_container:
|
2025-07-25 17:00:55 +08:00
|
|
|
|
item["end_pos"] = QPoint(1050, 420)
|
2025-07-25 15:18:05 +08:00
|
|
|
|
|
2025-07-15 16:30:50 +08:00
|
|
|
|
def start_animations(self):
|
|
|
|
|
|
"""启动完整动画序列"""
|
|
|
|
|
|
self.clear_animations()
|
|
|
|
|
|
self.start_logo_animations()
|
|
|
|
|
|
|
|
|
|
|
|
def clear_animations(self):
|
|
|
|
|
|
"""清理所有动画资源"""
|
|
|
|
|
|
for timer in self.timers:
|
|
|
|
|
|
timer.stop()
|
|
|
|
|
|
for anim in self.animations:
|
|
|
|
|
|
anim.stop()
|
|
|
|
|
|
self.timers.clear()
|
|
|
|
|
|
self.animations.clear()
|