地表附近支持右键环视(以点击点为球心,松开归位)

This commit is contained in:
2026-09-19 14:23:31 +08:00
parent 0715b38b81
commit 3bbfedeefa
+97
View File
@@ -114,6 +114,10 @@ function makeStickyNote(text, fontSize, selected = false) {
return { canvas: c, scale: 1 / S, width: cw, height: ch, tipX: B, tipY: B + h / 2 }; return { canvas: c, scale: 1 / S, width: cw, height: ch, tipX: B, tipY: B + h / 2 };
} }
// 右键“环视”手势:放大到地表附近时,右键拖动以点击点为球心环视,松开归位
const LOOK_GROUND_MAX_HEIGHT = 1e6; // 相机高度低于 1000 km 才启用
const LOOK_RATE = 0.005; // 每像素旋转弧度
export class CesiumGlobe { export class CesiumGlobe {
constructor({ onPick } = {}) { constructor({ onPick } = {}) {
this.onPick = onPick; this.onPick = onPick;
@@ -250,6 +254,7 @@ export class CesiumGlobe {
this._buildMarkers(); this._buildMarkers();
this._setupPick(); this._setupPick();
this._setupLookAround();
this._loadBoundaries(); this._loadBoundaries();
this._buildCityLabels(); this._buildCityLabels();
@@ -350,6 +355,98 @@ export class CesiumGlobe {
viewer.screenSpaceEventHandler.removeInputAction(Cesium.ScreenSpaceEventType.LEFT_DOUBLE_CLICK); viewer.screenSpaceEventHandler.removeInputAction(Cesium.ScreenSpaceEventType.LEFT_DOUBLE_CLICK);
} }
// 右键环视:地表附近右键拖动,以点击点为球心调整视角,松开归位
_setupLookAround() {
const viewer = this.viewer;
const scene = viewer.scene;
const camera = viewer.camera;
this._lookState = null;
this._orbitScratch = {
offset: new Cesium.Cartesian3(),
up: new Cesium.Cartesian3(),
right: new Cesium.Cartesian3(),
qAz: new Cesium.Quaternion(),
qEl: new Cesium.Quaternion(),
q: new Cesium.Quaternion(),
rotMat: new Cesium.Matrix3(),
newOffset: new Cesium.Cartesian3(),
newPos: new Cesium.Cartesian3(),
dir: new Cesium.Cartesian3(),
upProj: new Cesium.Cartesian3(),
};
const handler = new Cesium.ScreenSpaceEventHandler(scene.canvas);
handler.setInputAction((movement) => {
if (camera.positionCartographic.height > LOOK_GROUND_MAX_HEIGHT) return;
const position =
scene.pickPosition(movement.position) ||
camera.pickEllipsoid(movement.position, scene.globe.ellipsoid);
if (!Cesium.defined(position)) return;
this._lookState = {
pivot: Cesium.Cartesian3.clone(position),
position: Cesium.Cartesian3.clone(camera.position),
direction: Cesium.Cartesian3.clone(camera.direction),
up: Cesium.Cartesian3.clone(camera.up),
lastX: movement.position.x,
lastY: movement.position.y,
};
}, Cesium.ScreenSpaceEventType.RIGHT_DOWN);
handler.setInputAction((movement) => {
if (!this._lookState) return;
const s = this._lookState;
const dx = movement.position.x - s.lastX;
const dy = movement.position.y - s.lastY;
s.lastX = movement.position.x;
s.lastY = movement.position.y;
this._orbitAroundPivot(s.pivot, dx * LOOK_RATE, dy * LOOK_RATE);
}, Cesium.ScreenSpaceEventType.RIGHT_DRAG);
handler.setInputAction(() => {
if (!this._lookState) return;
const s = this._lookState;
camera.setView({
destination: s.position,
orientation: { direction: s.direction, up: s.up },
});
this._lookState = null;
}, Cesium.ScreenSpaceEventType.RIGHT_UP);
this._lookHandler = handler;
}
// 围绕 pivot 旋转相机(水平=方位角,垂直=俯仰角)
_orbitAroundPivot(pivot, dAzimuth, dElevation) {
const camera = this.viewer.camera;
const s = this._orbitScratch;
const offset = Cesium.Cartesian3.subtract(camera.position, pivot, s.offset);
const up = Cesium.Cartesian3.normalize(pivot, s.up); // 球心在原点,pivot 方向即法线
const right = Cesium.Cartesian3.normalize(
Cesium.Cartesian3.cross(camera.direction, camera.up, s.right),
s.right,
);
Cesium.Quaternion.fromAxisAngle(up, -dAzimuth, s.qAz);
Cesium.Quaternion.fromAxisAngle(right, -dElevation, s.qEl);
Cesium.Quaternion.multiply(s.qEl, s.qAz, s.q);
Cesium.Matrix3.fromQuaternion(s.q, s.rotMat);
Cesium.Matrix3.multiplyByVector(s.rotMat, offset, s.newOffset);
Cesium.Cartesian3.add(pivot, s.newOffset, s.newPos);
const dir = Cesium.Cartesian3.normalize(
Cesium.Cartesian3.subtract(pivot, s.newPos, s.dir),
s.dir,
);
const zDotDir = dir.z;
s.upProj.x = -dir.x * zDotDir;
s.upProj.y = -dir.y * zDotDir;
s.upProj.z = 1 - zDotDir * zDotDir;
Cesium.Cartesian3.normalize(s.upProj, s.upProj);
camera.setView({
destination: s.newPos,
orientation: { direction: dir, up: s.upProj },
});
}
// 相机平滑飞至某个标记上空并居中:回到与初始一致的高度与北朝上方向,左键拖拽手感不变 // 相机平滑飞至某个标记上空并居中:回到与初始一致的高度与北朝上方向,左键拖拽手感不变
flyToEntry(index) { flyToEntry(index) {
const entry = ENTRIES[index]; const entry = ENTRIES[index];