From b94abe32fab90344494d7bad37e7e6b15caca4ec Mon Sep 17 00:00:00 2001 From: fukunee Date: Sat, 19 Sep 2026 14:02:47 +0800 Subject: [PATCH] =?UTF-8?q?=E6=80=A7=E8=83=BD=E4=BC=98=E5=8C=96=EF=BC=9A?= =?UTF-8?q?=E7=B2=BE=E7=AE=80=E4=BE=BF=E7=AD=BE=E6=AF=8F=E5=B8=A7=E8=AE=A1?= =?UTF-8?q?=E7=AE=97=E3=80=81=E5=9C=B0=E7=90=86=E8=A7=86=E5=9B=BE=E8=B7=B3?= =?UTF-8?q?=E8=BF=87=20Three.js=20=E6=AF=8F=E5=B8=A7=E9=80=BB=E8=BE=91?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/core/App.js | 17 +++++++++++++++-- src/scene/CesiumGlobe.js | 23 ++++++++++++----------- 2 files changed, 27 insertions(+), 13 deletions(-) diff --git a/src/core/App.js b/src/core/App.js index 3e4e61f..1b49420 100644 --- a/src/core/App.js +++ b/src/core/App.js @@ -252,8 +252,11 @@ export class App { window.addEventListener('resize', () => this._onResize()); window.addEventListener('keydown', (e) => { - if (e.key === 'ArrowRight') this._step(1); - else if (e.key === 'ArrowLeft') this._step(-1); + if (e.key === 'ArrowRight' || e.key === 'ArrowLeft') { + const dir = e.key === 'ArrowRight' ? 1 : -1; + if (this.view === 'timeline') this._stepTimeline(dir); + else this._step(dir); + } else if (e.key === '1') this.switchView('binder'); else if (e.key === '2') this.switchView('timeline'); else if (e.key === '3') this.switchView('globe'); @@ -357,6 +360,15 @@ export class App { else this.binder.flipBackward(); } + // 时间轴:左右键选择上一张/下一张,动画与点击一致(居中 + 翻面) + _stepTimeline(dir) { + const next = clamp(this.selected + dir, 0, ENTRIES.length - 1); + if (next === this.selected) return; + this._select(next, { syncBinder: true }); + this._centerTimeline(next); + this.timeline.flip(next); + } + _select(index, { syncBinder = false } = {}) { this.selected = clamp(index, 0, ENTRIES.length - 1); const entry = ENTRIES[this.selected]; @@ -492,6 +504,7 @@ export class App { _animate() { requestAnimationFrame(this._animate); + if (this.view === 'globe') return; // 地理视图由 Cesium 渲染,跳过 Three.js 每帧逻辑 const dt = Math.min(this.clock.getDelta(), 0.05); // 是否有进行中的动画(决定本帧是否需要重绘) diff --git a/src/scene/CesiumGlobe.js b/src/scene/CesiumGlobe.js index e143df6..afc5183 100644 --- a/src/scene/CesiumGlobe.js +++ b/src/scene/CesiumGlobe.js @@ -275,7 +275,13 @@ export class CesiumGlobe { const position = Cesium.Cartesian3.fromDegrees(entry.lng, entry.lat); const noteNormal = makeStickyNote(entry.name, 16, false); const noteSelected = makeStickyNote(entry.name, 16, true); - this.markers.push({ position, noteNormal, noteSelected, index: i }); + this.markers.push({ + position, + r2: Cesium.Cartesian3.magnitudeSquared(position), + noteNormal, + noteSelected, + index: i, + }); }); this._initMarkerLayer(); } @@ -313,6 +319,7 @@ export class CesiumGlobe { _updateMarkerLayer() { const scene = this.viewer.scene; const camera = scene.camera; + const camPos = camera.positionWC; // 每帧只取一次相机位置 for (const m of this.markers) { const el = m.el; @@ -321,16 +328,10 @@ export class CesiumGlobe { const win = Cesium.SceneTransforms.worldToWindowCoordinates(scene, m.position); if (!win) { el.style.display = 'none'; continue; } - // 前/后半球判断 + 地平线附近渐变淡出(固定 ~3° 渐变带,避免拖拽时闪烁/突隐) - const dotPC = Cesium.Cartesian3.dot(m.position, camera.positionWC); - const pMag = Math.sqrt(Cesium.Cartesian3.magnitudeSquared(m.position)); - const cMag = Cesium.Cartesian3.magnitude(camera.positionWC); - const cosPhi = dotPC / (pMag * cMag); - const phi = Math.acos(Math.max(-1, Math.min(1, cosPhi))); - const phiHorizon = Math.acos(Math.max(-1, Math.min(1, pMag / cMag))); - const margin = phiHorizon - phi; // > 0 为前半球 - if (margin <= 0) { el.style.display = 'none'; continue; } - const opacity = Math.min(1, margin / 0.05); // 0.05 rad ≈ 3° + // 前/后半球:dot(P, C) > |P|²;地平线附近用 frontness 渐变淡出(避免 acos) + const frontness = Cesium.Cartesian3.dot(m.position, camPos) - m.r2; + if (frontness <= 0) { el.style.display = 'none'; continue; } + const opacity = Math.min(1, frontness / (m.r2 * 0.02)); el.style.display = 'block'; el.style.opacity = String(opacity);