diff --git a/src/scene/CesiumGlobe.js b/src/scene/CesiumGlobe.js index b0f5a65..a3a00f9 100644 --- a/src/scene/CesiumGlobe.js +++ b/src/scene/CesiumGlobe.js @@ -223,6 +223,10 @@ export class CesiumGlobe { modifier: Cesium.KeyboardEventModifier.CTRL, }, ]; + // 关闭相机惯性:避免左键旋转释放后的余力在右键环视时叠加,产生“两套机制打架” + controller.inertiaSpin = 0; + controller.inertiaTranslate = 0; + controller.inertiaZoom = 0; // 阻止右键弹出浏览器菜单 scene.canvas.addEventListener('contextmenu', (e) => e.preventDefault()); @@ -360,65 +364,17 @@ export class CesiumGlobe { viewer.screenSpaceEventHandler.removeInputAction(Cesium.ScreenSpaceEventType.LEFT_DOUBLE_CLICK); } - // 右键环视:以点击处为球心,左右旋转 ±40°、上下倾角 ±20°,松开自动归位。 - // 不用原生 tilt(RIGHT_DRAG):原生 tilt 会绕点击点做轨道移动,heading/pitch - // 是相对“不断移动的相机位置”计算的,无法干净地限制成 40°/20°。 + // 右键环视:相机“原地倾倒”(位置不动,只改视线方向)。左右 = 经度方向、上下 = 纬度方向,强制北朝上。 + // 关键:直接算 heading/pitch 传给 setView,绕过 setView 在近天底处用 up 反推 heading 的奇点—— + // 否则初期的小角度倾斜会被翻转(表现为“向北平移”)。 _setupLookAround() { const scene = this.viewer.scene; const camera = this.viewer.camera; const handler = new Cesium.ScreenSpaceEventHandler(scene.canvas); this._lookState = null; - this._orbitScratch = { - offset: new Cesium.Cartesian3(), - upAxis: new Cesium.Cartesian3(), - eastAxis: new Cesium.Cartesian3(), - northAxis: 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(), - }; handler.setInputAction((movement) => { - // 旋转中心 = 相机当前注视的屏幕中心点(与原生 tilt 一致), - // 而不是点击点:用点击点会导致一拖动就“瞬移”到点击处。 - // 取“地形表面”点而不是椭球点:相机看到的是地形,若用椭球点做球心, - // 视觉中心(地形)会随旋转漂移,产生“两套体系”的错觉。 - const canvas = scene.canvas; - const center = Cesium.Cartesian2.fromElements( - canvas.clientWidth / 2, - canvas.clientHeight / 2, - ); - const ray = camera.getPickRay(center); - let pivot = scene.globe.pick(ray, scene); - if (!Cesium.defined(pivot)) { - pivot = camera.pickEllipsoid(center, scene.globe.ellipsoid); - } - if (!Cesium.defined(pivot)) return; - const sc = this._orbitScratch; - const offset = Cesium.Cartesian3.subtract(camera.position, pivot, sc.offset); - const upAxis = Cesium.Cartesian3.normalize(pivot, sc.upAxis); - const eastAxis = Cesium.Cartesian3.cross(Cesium.Cartesian3.UNIT_Z, pivot, sc.eastAxis); // 绕它转 = 上下拖动(纬度方向倾倒) - if (Cesium.Cartesian3.magnitudeSquared(eastAxis) < 1e-12) { - eastAxis.x = 1; - eastAxis.y = 0; - eastAxis.z = 0; // 极点退化兜底 - } else { - Cesium.Cartesian3.normalize(eastAxis, eastAxis); - } - const northAxis = Cesium.Cartesian3.normalize( - Cesium.Cartesian3.cross(upAxis, eastAxis, sc.northAxis), // 绕它转 = 左右拖动(经度方向倾倒) - sc.northAxis, - ); this._lookState = { - pivot: Cesium.Cartesian3.clone(pivot), - startOffset: Cesium.Cartesian3.clone(offset), - eastAxis: Cesium.Cartesian3.clone(eastAxis), - northAxis: Cesium.Cartesian3.clone(northAxis), position: Cesium.Cartesian3.clone(camera.position), direction: Cesium.Cartesian3.clone(camera.direction), up: Cesium.Cartesian3.clone(camera.up), @@ -438,7 +394,7 @@ export class CesiumGlobe { s.lastY = movement.endPosition.y; s.azimuth = Cesium.Math.clamp(s.azimuth + dx * LOOK_RATE, -MAX_LOOK_ROT, MAX_LOOK_ROT); s.elevation = Cesium.Math.clamp(s.elevation + dy * LOOK_RATE, -MAX_LOOK_TILT, MAX_LOOK_TILT); - this._orbitCamera(s); + this._tiltCamera(s); }, Cesium.ScreenSpaceEventType.MOUSE_MOVE); handler.setInputAction(() => { @@ -454,37 +410,15 @@ export class CesiumGlobe { this._lookHandler = handler; } - // 按累计角度(已限制)把相机绕点击点倾倒:左右 = 经度方向(绕南北轴), - // 上下 = 纬度方向(绕东西轴);up 始终强制北朝上。 - _orbitCamera(s) { - const camera = this.viewer.camera; - const sc = this._orbitScratch; - Cesium.Quaternion.fromAxisAngle(s.northAxis, s.azimuth, sc.qAz); - Cesium.Quaternion.fromAxisAngle(s.eastAxis, s.elevation, sc.qEl); - Cesium.Quaternion.multiply(sc.qEl, sc.qAz, sc.q); - Cesium.Matrix3.fromQuaternion(sc.q, sc.rotMat); - Cesium.Matrix3.multiplyByVector(sc.rotMat, s.startOffset, sc.newOffset); - Cesium.Cartesian3.add(s.pivot, sc.newOffset, sc.newPos); - const dir = Cesium.Cartesian3.normalize( - Cesium.Cartesian3.subtract(s.pivot, sc.newPos, sc.dir), - sc.dir, - ); - // 强制北朝上:up = 世界 Z 投影到垂直于视线的方向 - const z = dir.z; - sc.upProj.x = -dir.x * z; - sc.upProj.y = -dir.y * z; - sc.upProj.z = 1 - z * z; - if (Cesium.Cartesian3.magnitudeSquared(sc.upProj) < 1e-12) { - Cesium.Cartesian3.clone(s.up, sc.upProj); // 垂直俯视时北朝上无定义,回退起始 up - } else { - Cesium.Cartesian3.normalize(sc.upProj, sc.upProj); - } - camera.setView({ - destination: Cesium.Cartesian3.clone(sc.newPos), - orientation: { - direction: Cesium.Cartesian3.clone(dir), - up: Cesium.Cartesian3.clone(sc.upProj), - }, + // 原地倾倒:由累计的经度角(azimuth)/纬度角(elevation)直接求 heading/pitch,roll 固定 0(北朝上) + _tiltCamera(s) { + const el = s.elevation; + const az = s.azimuth; + const heading = Math.atan2(-Math.cos(el) * Math.sin(az), Math.sin(el)); + const pitch = Math.asin(-Math.cos(el) * Math.cos(az)); + this.viewer.camera.setView({ + destination: s.position, + orientation: { heading, pitch, roll: 0 }, }); }