Replace Three.js globe with Cesium
Migrate the globe view to a Cesium viewer with Esri satellite imagery and terrain, dropping the custom blue-marble texture and dot-bead rendering. Add vite-plugin-cesium and a container element for the viewer. Also rework the binder leather and suede materials with dedicated normal maps, and refine timeline labels and page hole sizing.
This commit is contained in:
+41
-11
@@ -3,13 +3,13 @@ import { OrbitControls } from 'three/addons/controls/OrbitControls.js';
|
||||
import { ENTRIES } from '../data/entries.js';
|
||||
import { Binder } from '../scene/binder.js';
|
||||
import { Timeline, TIMELINE_SPREAD } from '../scene/timeline.js';
|
||||
import { Globe } from '../scene/globe.js';
|
||||
import { CesiumGlobe } from '../scene/CesiumGlobe.js';
|
||||
import { Overlay } from '../ui/overlay.js';
|
||||
import { clamp, easeInOutCubic } from '../utils/easing.js';
|
||||
|
||||
const CAMERA_POSES = {
|
||||
binder: { pos: new THREE.Vector3(0.2, 0.75, 2.95), target: new THREE.Vector3(0, 0.02, 0), max: 6, min: 1.6 },
|
||||
timeline: { pos: new THREE.Vector3(0, 1.4, 7.9), target: new THREE.Vector3(0, 0, 0), max: 13, min: 3.4 },
|
||||
timeline: { pos: new THREE.Vector3(0, 1.4, 7.9), target: new THREE.Vector3(0, 0, 0), max: 13, min: 1.5 },
|
||||
globe: { pos: new THREE.Vector3(0, 0.35, 3.7), target: new THREE.Vector3(0, 0, 0), max: 8, min: 2.2 },
|
||||
};
|
||||
|
||||
@@ -69,7 +69,7 @@ export class App {
|
||||
}
|
||||
|
||||
_initLights() {
|
||||
const hemi = new THREE.HemisphereLight(0xfff1d8, 0x221811, 0.55);
|
||||
const hemi = new THREE.HemisphereLight(0xfff1d8, 0x2a1f14, 0.8);
|
||||
this.scene.add(hemi);
|
||||
|
||||
const key = new THREE.DirectionalLight(0xffe6c0, 2.4);
|
||||
@@ -89,6 +89,16 @@ export class App {
|
||||
const rim = new THREE.DirectionalLight(0x8fa0d0, 0.7);
|
||||
rim.position.set(-2.5, 1.2, -3.5);
|
||||
this.scene.add(rim);
|
||||
|
||||
// 低角度掠射光:横扫皮面,凸显皮革纹理凹凸
|
||||
const graze = new THREE.DirectionalLight(0xffd9a8, 1.1);
|
||||
graze.position.set(-4.5, 0.7, 2.6);
|
||||
this.scene.add(graze);
|
||||
|
||||
// 背面补光:让外侧牛皮从背面看也可见
|
||||
const backLight = new THREE.DirectionalLight(0xffd9a8, 0.55);
|
||||
backLight.position.set(0.5, 1.2, -3.5);
|
||||
this.scene.add(backLight);
|
||||
}
|
||||
|
||||
_initScene() {
|
||||
@@ -96,16 +106,14 @@ export class App {
|
||||
this.binder.onChange = (i) => this._select(i, { syncBinder: false });
|
||||
|
||||
this.timeline = new Timeline();
|
||||
this.globe = new Globe({ renderer: this.renderer });
|
||||
this.globe = new CesiumGlobe({ onPick: (i) => this._pickGlobe(i) });
|
||||
|
||||
this.scene.add(this.binder.group);
|
||||
this.scene.add(this.timeline.group);
|
||||
this.scene.add(this.globe.group);
|
||||
|
||||
this._buildGround();
|
||||
|
||||
this.timeline.group.visible = false;
|
||||
this.globe.group.visible = false;
|
||||
|
||||
this._collectInteractives();
|
||||
}
|
||||
@@ -148,7 +156,7 @@ export class App {
|
||||
});
|
||||
});
|
||||
this.timeline.cards.forEach((c) => this.interactives.timeline.push(c.card));
|
||||
this.globe.markers.forEach((m) => this.interactives.globe.push(m.marker));
|
||||
// 地理视图由 Cesium 自行拾取,不参与 Three.js 射线检测
|
||||
}
|
||||
|
||||
_initEvents() {
|
||||
@@ -237,6 +245,13 @@ export class App {
|
||||
return hits.length ? hits[0] : null;
|
||||
}
|
||||
|
||||
_pickGlobe(index) {
|
||||
this._select(index, { syncBinder: false });
|
||||
// 点击地理标记时,显示右下角登记卡详情
|
||||
document.getElementById('detail-card').classList.remove('hidden');
|
||||
this.binder.syncTo(index);
|
||||
}
|
||||
|
||||
_pick() {
|
||||
const hit = this._hitTest();
|
||||
if (!hit) return;
|
||||
@@ -280,9 +295,24 @@ export class App {
|
||||
this.view = view;
|
||||
this.overlay.setView(view);
|
||||
|
||||
const isGlobe = view === 'globe';
|
||||
|
||||
this.binder.group.visible = view === 'binder';
|
||||
this.timeline.group.visible = view === 'timeline';
|
||||
this.globe.group.visible = view === 'globe';
|
||||
|
||||
// Cesium 地球:地理视图显示 Cesium 画布,其余视图显示 Three.js 画布
|
||||
document.getElementById('scene').style.display = isGlobe ? 'none' : 'block';
|
||||
document.getElementById('cesium-container').style.display = isGlobe ? 'block' : 'none';
|
||||
|
||||
if (isGlobe) {
|
||||
this.globe.ensureCreated();
|
||||
this.globe.setVisible(true);
|
||||
this.globe.setSelected(this.selected);
|
||||
this.globe.resize();
|
||||
} else {
|
||||
this.globe.setVisible(false);
|
||||
this.renderer.setSize(window.innerWidth, window.innerHeight);
|
||||
}
|
||||
|
||||
// 登记卡详情仅在活页本视图展示,避免遮挡时间轴/地理内容
|
||||
document.getElementById('detail-card').classList.toggle('hidden', view !== 'binder');
|
||||
@@ -295,8 +325,7 @@ export class App {
|
||||
this.controls.enableRotate = !locked;
|
||||
this.controls.enablePan = !locked;
|
||||
this.controls.enableZoom = true;
|
||||
this.controls.autoRotate = view === 'globe';
|
||||
this.controls.autoRotateSpeed = 0.5;
|
||||
this.controls.autoRotate = false;
|
||||
|
||||
// 进入时间轴时复位横向偏移
|
||||
if (view === 'timeline') {
|
||||
@@ -304,7 +333,7 @@ export class App {
|
||||
this.timeline.group.position.x = 0;
|
||||
}
|
||||
|
||||
this._tweenCamera(pose.pos, pose.target);
|
||||
if (!isGlobe) this._tweenCamera(pose.pos, pose.target);
|
||||
}
|
||||
|
||||
_tweenCamera(toPos, toTarget) {
|
||||
@@ -320,6 +349,7 @@ export class App {
|
||||
this.camera.aspect = window.innerWidth / window.innerHeight;
|
||||
this.camera.updateProjectionMatrix();
|
||||
this.renderer.setSize(window.innerWidth, window.innerHeight);
|
||||
if (this.globe) this.globe.resize();
|
||||
}
|
||||
|
||||
_animate() {
|
||||
|
||||
Reference in New Issue
Block a user