性能优化:Cesium 懒加载+后台预载、按需渲染、射线检测节流、城市标签 primitive 化、时间轴纹理跟随可视区域

- Cesium 改为动态加载(vite 插件不再注入 6MB 脚本),首屏零 Cesium 请求
- 首帧渲染后后台预载地理视图,loading 展示 8 秒期间并行,就绪前地理按钮置灰
- Three.js 按需渲染:静止停止重绘,动画/交互/状态变化时渲染
- 射线检测合帧节流,binder 只测当前页±1、时间轴只测可视窗口
- 移除 vignette 的 mix-blend-mode 全屏合成
- 7342 个城市从 entity 改为 PointPrimitiveCollection + LabelCollection
- 时间轴懒加载窗口跟随可视区域(拖拽/缩放实时跟随)
This commit is contained in:
2026-09-19 03:21:27 +08:00
parent 0c7990a9a8
commit f701862e5a
8 changed files with 175 additions and 32 deletions
+31 -2
View File
@@ -27,6 +27,7 @@ export class Timeline {
this._visRange = null;
this._ready = false;
this._pending = [];
this._dirty = false;
this._buildLine();
this._buildCards();
@@ -141,17 +142,21 @@ export class Timeline {
c.material.setPageMaps(this.placeholder.front, this.placeholder.back);
c.isPlaceholder = true;
this._textured.delete(i);
this._dirty = true;
}
// 进入窗口:分帧异步生成,避免一次性生成 25 页阻塞主线程
this._pending = [...win].filter((i) => !this._textured.has(i) && this.cards[i].isPlaceholder);
const next = [...win].filter((i) => !this._textured.has(i) && this.cards[i].isPlaceholder);
if (next.length) this._dirty = true;
this._pending = next;
this.drainPending();
}
// 每帧由 App._animate 驱动,逐批生成窗口内纹理(避免阻塞)
drainPending() {
if (!this._pending || !this._pending.length) return;
if (!this._pending || !this._pending.length) return false;
const budget = 2; // 每帧最多生成 2 页
let did = false;
for (let k = 0; k < budget && this._pending.length; k++) {
const i = this._pending.shift();
const c = this.cards[i];
@@ -160,7 +165,21 @@ export class Timeline {
c.material.setPageMaps(front, back);
c.isPlaceholder = false;
this._textured.add(i);
this._dirty = true;
did = true;
}
return did;
}
hasActiveFlip() {
return this.cards.some((c) => c.flipAnim);
}
// App 每帧消费“纹理窗口变化”标志,用于按需渲染
consumeDirty() {
const d = this._dirty;
this._dirty = false;
return d;
}
// 返回某张卡片在时间轴组内的横向坐标(用于点击后居中)
@@ -168,6 +187,16 @@ export class Timeline {
return cardX(index, ENTRIES.length);
}
// 射线检测用:只返回可视窗口内的卡片 mesh(减少求交对象)
pickableMeshes() {
if (!this._visRange) return this.cards.map((c) => c.mesh);
const list = [];
for (let i = this._visRange.min; i <= this._visRange.max; i++) {
if (this.cards[i]) list.push(this.cards[i].mesh);
}
return list;
}
// 点击翻转:每次点击切换正/反面;选中另一张时,其它翻到背面的卡片先翻回正面
flip(index) {
this.cards.forEach((o) => {