From 0715b38b8171ab92cc62dadb419403ab8bcebe29 Mon Sep 17 00:00:00 2001 From: fukunee Date: Sat, 19 Sep 2026 14:20:37 +0800 Subject: [PATCH] =?UTF-8?q?=E5=9C=B0=E7=90=86=E8=A7=86=E5=9B=BE=E7=AD=89?= =?UTF-8?q?=E7=93=A6=E7=89=87=E5=8A=A0=E8=BD=BD=E5=AE=8C=E5=86=8D=E6=B7=A1?= =?UTF-8?q?=E5=85=A5=EF=BC=8C=E9=81=BF=E5=85=8D=E9=80=90=E5=9D=97=E5=BC=B9?= =?UTF-8?q?=E5=87=BA?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/core/App.js | 30 +++++++++++++++++++++++------- src/scene/CesiumGlobe.js | 18 ++++++++++++++++++ 2 files changed, 41 insertions(+), 7 deletions(-) diff --git a/src/core/App.js b/src/core/App.js index 1b49420..0b5be5c 100644 --- a/src/core/App.js +++ b/src/core/App.js @@ -54,6 +54,9 @@ export class App { this.cameraTween = null; this._animate = this._animate.bind(this); requestAnimationFrame(this._animate); + + // 首屏渲染后,分帧生成时间轴文字标签(避免阻塞首屏加载环) + requestAnimationFrame(() => requestAnimationFrame(() => this.timeline.initLabels())); } // 首屏加载完成后由 main.js 调用:后台预载地理视图,就绪后激活“地理”按钮 @@ -208,6 +211,7 @@ export class App { if (this.view === 'timeline') { // 时间轴:按下开始拖拽左右滑动;未移动的抬起才视为点击选中 this.timelineSlide = null; // 手动拖拽时取消居中动画 + this.timeline.setPausedGeneration(true); // 拖拽期间暂停纹理生成,保持流畅 this._drag = { active: true, startX: e.clientX, startY: e.clientY, moved: false, offset: this.timelineOffset }; canvas.setPointerCapture(e.pointerId); } else { @@ -240,6 +244,7 @@ export class App { const wasClick = !this._drag.moved; this._drag.active = false; canvas.style.cursor = 'grab'; + this.timeline.setPausedGeneration(false); // 拖拽结束,恢复纹理生成 if (wasClick) this._pick(); } }); @@ -247,6 +252,7 @@ export class App { canvas.addEventListener('pointercancel', () => { if (this._drag) this._drag.active = false; canvas.style.cursor = 'grab'; + this.timeline.setPausedGeneration(false); }); window.addEventListener('resize', () => this._onResize()); @@ -328,10 +334,12 @@ export class App { const { view, index } = hit.object.userData; if (view === 'binder') { const cur = this.binder.current; - if (index === cur) { - this.binder.flipForward(); // 点击当前右页:翻到下一页 + const f = this.binder.flip; + // 翻页进行中:把“正在翻的页”与“即将露出的下一页”都视为向前翻的目标,点击不阻断 + if (index === cur || (f && index === f.nextCurrent)) { + this.binder.flipForward(); } else if (index === cur - 1) { - this.binder.flipBackward(); // 点击左页:翻回上一页 + this.binder.flipBackward(); } else { this._select(index, { syncBinder: true }); } @@ -395,10 +403,15 @@ export class App { iris.style.opacity = '1'; const doSwap = () => { this._setView(view); - // 等新视图渲染一两帧后再淡出,避免半透明时看到切换瞬间 - requestAnimationFrame(() => { - requestAnimationFrame(() => { iris.style.opacity = '0'; }); - }); + if (view === 'globe') { + // 地理视图:等影像/地形瓦片加载完成再淡出,让球体与标记/标签一起出现 + this.globe.waitUntilViewReady(4000).then(() => { iris.style.opacity = '0'; }); + } else { + // 等新视图渲染一两帧后再淡出,避免半透明时看到切换瞬间 + requestAnimationFrame(() => { + requestAnimationFrame(() => { iris.style.opacity = '0'; }); + }); + } }; setTimeout(doSwap, 200); // 与 0.18s 的淡入匹配,完全变黑后再切换 } @@ -412,6 +425,9 @@ export class App { this.binder.group.visible = view === 'binder'; this.timeline.group.visible = view === 'timeline'; + // 阴影仅在活页本视图启用:时间轴/地理无投影,降低滑动时阴影贴图重绘开销 + this.renderer.shadowMap.enabled = view === 'binder'; + // Cesium 地球:地理视图显示 Cesium 画布,其余视图显示 Three.js 画布 document.getElementById('scene').style.display = isGlobe ? 'none' : 'block'; document.getElementById('cesium-container').classList.toggle('visible', isGlobe); diff --git a/src/scene/CesiumGlobe.js b/src/scene/CesiumGlobe.js index 83e973d..c3c2523 100644 --- a/src/scene/CesiumGlobe.js +++ b/src/scene/CesiumGlobe.js @@ -510,6 +510,24 @@ export class CesiumGlobe { if (visible && this.viewer) this.viewer.scene.requestRender(); } + // 等待当前视图的影像/地形瓦片加载完成(超时兜底),用于切换时“一起出现” + waitUntilViewReady(timeout = 4000) { + return new Promise((resolve) => { + const globe = this.viewer.scene.globe; + let done = false; + const finish = () => { + if (done) return; + done = true; + globe.tileLoadProgressEvent.removeEventListener(onProgress); + resolve(); + }; + const onProgress = (remaining) => { if (remaining <= 0) finish(); }; + globe.tileLoadProgressEvent.addEventListener(onProgress); + if (globe.tilesLoaded) finish(); + setTimeout(finish, timeout); + }); + } + resize() { if (this.viewer) this.viewer.resize(); }