From b92bb4985d2150bbbc0a696c9dc97e0e593ee4d5 Mon Sep 17 00:00:00 2001 From: KQL Date: Thu, 4 Dec 2025 20:02:38 +0800 Subject: [PATCH] =?UTF-8?q?fix:=20=E5=BD=BB=E5=BA=95=E4=BF=AE=E5=A4=8DGSAP?= =?UTF-8?q?=E5=8A=A8=E7=94=BB=E6=B3=84=E6=BC=8F=E5=AF=BC=E8=87=B4=E7=9A=84?= =?UTF-8?q?=E8=87=AA=E5=8A=A8=E8=BD=AC=E5=9C=BA=E9=97=AE=E9=A2=98?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 关键修复: - 在 SceneManager 中保存 animationFrameId 和 introTimeline 引用 - 在 Transition 中保存 timeline 引用 - dispose() 时彻底清理所有 GSAP 动画: * 停止 requestAnimationFrame 循环 * 杀掉开场动画 timeline * 杀掉转场动画 timeline * 杀掉所有针对相机、地球的补间动画 - 添加 Transition.dispose() 方法 - 防止旧动画回调触发导致意外转场 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude --- js/scene/SceneManager.js | 42 ++++++++++++++++++++++++++++++++++++++-- js/scene/Transition.js | 16 +++++++++++++-- 2 files changed, 54 insertions(+), 4 deletions(-) diff --git a/js/scene/SceneManager.js b/js/scene/SceneManager.js index 86be696..a7cab50 100644 --- a/js/scene/SceneManager.js +++ b/js/scene/SceneManager.js @@ -30,6 +30,10 @@ export class SceneManager { this.earthModel = null; this.transition = null; + // 动画控制 + this.animationFrameId = null; + this.introTimeline = null; + this.init(); } @@ -97,12 +101,15 @@ export class SceneManager { startIntroSequence(uiElements) { const animConfig = CONFIG.animation.intro; - const tl = gsap.timeline({ + // 保存 timeline 引用,以便在 dispose 时清理 + this.introTimeline = gsap.timeline({ onComplete: () => { this.isIntro = false; // 动画结束,允许交互 } }); + const tl = this.introTimeline; + // A. 摄像机缓慢推进 (从 z=100 -> z=16) tl.to(this.camera.position, { z: CONFIG.scene.camera.defaultZ, @@ -279,7 +286,7 @@ export class SceneManager { // 动画循环 animate() { - requestAnimationFrame(() => this.animate()); + this.animationFrameId = requestAnimationFrame(() => this.animate()); // 如果不在转场中 if (!this.transition || !this.transition.isActive()) { @@ -299,6 +306,31 @@ export class SceneManager { // 销毁场景 dispose() { + // 停止动画循环 + if (this.animationFrameId) { + cancelAnimationFrame(this.animationFrameId); + this.animationFrameId = null; + } + + // 杀掉所有正在运行的 GSAP 动画 + if (this.introTimeline) { + this.introTimeline.kill(); + this.introTimeline = null; + } + + // 杀掉转场相关的 GSAP 动画(杀掉所有针对相机、地球等对象的动画) + if (this.camera) { + gsap.killTweensOf(this.camera.position); + } + if (this.earthModel) { + const earthGroup = this.earthModel.getGroup(); + const clouds = this.earthModel.getClouds(); + const atmosphere = this.earthModel.getAtmosphere(); + if (earthGroup) gsap.killTweensOf(earthGroup.rotation); + if (clouds) gsap.killTweensOf(clouds.material); + if (atmosphere) gsap.killTweensOf(atmosphere.material); + } + // 移除事件监听器,防止内存泄漏和重复触发 if (this.eventHandlers) { window.removeEventListener('mousemove', this.eventHandlers.mouseMove); @@ -308,6 +340,12 @@ export class SceneManager { window.removeEventListener('resize', this.eventHandlers.resize); } + // 清理转场动画 + if (this.transition) { + this.transition.dispose(); + this.transition = null; + } + if (this.starSystem) this.starSystem.dispose(); if (this.earthModel) this.earthModel.dispose(); if (this.renderer) { diff --git a/js/scene/Transition.js b/js/scene/Transition.js index 61177d6..da7fcef 100644 --- a/js/scene/Transition.js +++ b/js/scene/Transition.js @@ -11,6 +11,7 @@ export class Transition { this.uiElements = uiElements; // { uiLayer, hint, speedLines, cloudFog } this.onComplete = onComplete; this.isTransitioning = false; + this.timeline = null; // 保存 timeline 引用以便清理 } // 触发超空间跳跃转场 @@ -29,14 +30,16 @@ export class Transition { const clouds = this.earthModel.getClouds(); const atmosphere = this.earthModel.getAtmosphere(); - // 创建GSAP时间线 - const tl = gsap.timeline({ + // 创建GSAP时间线(保存引用以便清理) + this.timeline = gsap.timeline({ onComplete: () => { this.isTransitioning = false; if (this.onComplete) this.onComplete(); } }); + const tl = this.timeline; + // 转场配置 const warpConfig = CONFIG.warp; const transConfig = CONFIG.animation.transition; @@ -101,4 +104,13 @@ export class Transition { isActive() { return this.isTransitioning; } + + // 清理转场动画 + dispose() { + if (this.timeline) { + this.timeline.kill(); + this.timeline = null; + } + this.isTransitioning = false; + } }