右键环视改为手动绕点击点旋转,精确限制 ±40°/±20°,松开归位
This commit is contained in:
+91
-42
@@ -26,6 +26,11 @@ const IMAGERY_URL =
|
|||||||
const TERRAIN_URL =
|
const TERRAIN_URL =
|
||||||
'https://elevation3d.arcgis.com/arcgis/rest/services/WorldElevation3D/Terrain3D/ImageServer';
|
'https://elevation3d.arcgis.com/arcgis/rest/services/WorldElevation3D/Terrain3D/ImageServer';
|
||||||
|
|
||||||
|
// 右键环视角度限制与灵敏度
|
||||||
|
const MAX_LOOK_ROT = (40 * Math.PI) / 180; // 左右旋转 ±40°
|
||||||
|
const MAX_LOOK_TILT = (20 * Math.PI) / 180; // 上下倾角 ±20°
|
||||||
|
const LOOK_RATE = Math.PI / 720; // 每像素约 0.25°
|
||||||
|
|
||||||
// 计算多边形集合的球面面积(km²),用于按国土面积分级国家名字号
|
// 计算多边形集合的球面面积(km²),用于按国土面积分级国家名字号
|
||||||
function sphericalAreaKm2(polys) {
|
function sphericalAreaKm2(polys) {
|
||||||
let area = 0;
|
let area = 0;
|
||||||
@@ -212,7 +217,6 @@ export class CesiumGlobe {
|
|||||||
Cesium.CameraEventType.PINCH,
|
Cesium.CameraEventType.PINCH,
|
||||||
];
|
];
|
||||||
controller.tiltEventTypes = [
|
controller.tiltEventTypes = [
|
||||||
Cesium.CameraEventType.RIGHT_DRAG, // 传统右键:以点击处为球心倾斜视角
|
|
||||||
Cesium.CameraEventType.PINCH,
|
Cesium.CameraEventType.PINCH,
|
||||||
{
|
{
|
||||||
eventType: Cesium.CameraEventType.LEFT_DRAG,
|
eventType: Cesium.CameraEventType.LEFT_DRAG,
|
||||||
@@ -226,7 +230,7 @@ export class CesiumGlobe {
|
|||||||
// 精细化渲染:质量与性能的平衡
|
// 精细化渲染:质量与性能的平衡
|
||||||
scene.globe.maximumScreenSpaceError = 2.0; // 默认细分,最省性能
|
scene.globe.maximumScreenSpaceError = 2.0; // 默认细分,最省性能
|
||||||
scene.globe.tileCacheSize = 600; // 更大的瓦片缓存,减少细节刷新
|
scene.globe.tileCacheSize = 600; // 更大的瓦片缓存,减少细节刷新
|
||||||
scene.fxaa = false; // 关闭抗锯齿后处理,省一个全屏通道
|
scene.fxaa = true; // 开启抗锯齿
|
||||||
viewer.resolutionScale = 1.0; // 关闭超采样(DPR 已提供足够分辨率),省填充率
|
viewer.resolutionScale = 1.0; // 关闭超采样(DPR 已提供足够分辨率),省填充率
|
||||||
scene.maximumRenderTimeChange = Infinity; // 配合 requestRenderMode:静止时停止重绘,大幅省电省帧
|
scene.maximumRenderTimeChange = Infinity; // 配合 requestRenderMode:静止时停止重绘,大幅省电省帧
|
||||||
|
|
||||||
@@ -255,7 +259,7 @@ export class CesiumGlobe {
|
|||||||
|
|
||||||
this._buildMarkers();
|
this._buildMarkers();
|
||||||
this._setupPick();
|
this._setupPick();
|
||||||
this._setupRightRestore();
|
this._setupLookAround();
|
||||||
this._loadBoundaries();
|
this._loadBoundaries();
|
||||||
this._buildCityLabels();
|
this._buildCityLabels();
|
||||||
|
|
||||||
@@ -356,65 +360,110 @@ export class CesiumGlobe {
|
|||||||
viewer.screenSpaceEventHandler.removeInputAction(Cesium.ScreenSpaceEventType.LEFT_DOUBLE_CLICK);
|
viewer.screenSpaceEventHandler.removeInputAction(Cesium.ScreenSpaceEventType.LEFT_DOUBLE_CLICK);
|
||||||
}
|
}
|
||||||
|
|
||||||
// 右键操作(原生 tilt)松开后自动归位 + 拖动角度限制(左右旋转 ±40°,上下倾角 ±20°)
|
// 右键环视:以点击处为球心,左右旋转 ±40°、上下倾角 ±20°,松开自动归位。
|
||||||
_setupRightRestore() {
|
// 不用原生 tilt(RIGHT_DRAG):原生 tilt 会绕点击点做轨道移动,heading/pitch
|
||||||
|
// 是相对“不断移动的相机位置”计算的,无法干净地限制成 40°/20°。
|
||||||
|
_setupLookAround() {
|
||||||
const scene = this.viewer.scene;
|
const scene = this.viewer.scene;
|
||||||
const camera = this.viewer.camera;
|
const camera = this.viewer.camera;
|
||||||
const handler = new Cesium.ScreenSpaceEventHandler(scene.canvas);
|
const handler = new Cesium.ScreenSpaceEventHandler(scene.canvas);
|
||||||
this._rightRestore = null;
|
this._lookState = null;
|
||||||
|
this._orbitScratch = {
|
||||||
|
offset: new Cesium.Cartesian3(),
|
||||||
|
upAxis: new Cesium.Cartesian3(),
|
||||||
|
eastAxis: 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(() => {
|
handler.setInputAction((movement) => {
|
||||||
this._rightRestore = {
|
const pivot =
|
||||||
|
scene.pickPosition(movement.position) ||
|
||||||
|
camera.pickEllipsoid(movement.position, 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);
|
||||||
|
}
|
||||||
|
this._lookState = {
|
||||||
|
pivot: Cesium.Cartesian3.clone(pivot),
|
||||||
|
startOffset: Cesium.Cartesian3.clone(offset),
|
||||||
|
upAxis: Cesium.Cartesian3.clone(upAxis),
|
||||||
|
eastAxis: Cesium.Cartesian3.clone(eastAxis),
|
||||||
position: Cesium.Cartesian3.clone(camera.position),
|
position: Cesium.Cartesian3.clone(camera.position),
|
||||||
direction: Cesium.Cartesian3.clone(camera.direction),
|
direction: Cesium.Cartesian3.clone(camera.direction),
|
||||||
up: Cesium.Cartesian3.clone(camera.up),
|
up: Cesium.Cartesian3.clone(camera.up),
|
||||||
heading: camera.heading,
|
azimuth: 0,
|
||||||
pitch: camera.pitch,
|
elevation: 0,
|
||||||
|
lastX: movement.position.x,
|
||||||
|
lastY: movement.position.y,
|
||||||
};
|
};
|
||||||
}, Cesium.ScreenSpaceEventType.RIGHT_DOWN);
|
}, Cesium.ScreenSpaceEventType.RIGHT_DOWN);
|
||||||
|
|
||||||
|
handler.setInputAction((movement) => {
|
||||||
|
if (!this._lookState) return;
|
||||||
|
const s = this._lookState;
|
||||||
|
const dx = movement.endPosition.x - s.lastX;
|
||||||
|
const dy = movement.endPosition.y - s.lastY;
|
||||||
|
s.lastX = movement.endPosition.x;
|
||||||
|
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);
|
||||||
|
}, Cesium.ScreenSpaceEventType.MOUSE_MOVE);
|
||||||
|
|
||||||
handler.setInputAction(() => {
|
handler.setInputAction(() => {
|
||||||
if (!this._rightRestore) return;
|
if (!this._lookState) return;
|
||||||
const s = this._rightRestore;
|
const s = this._lookState;
|
||||||
this._rightRestore = null;
|
this._lookState = null;
|
||||||
camera.setView({
|
camera.setView({
|
||||||
destination: s.position,
|
destination: s.position,
|
||||||
orientation: { direction: s.direction, up: s.up },
|
orientation: { direction: s.direction, up: s.up },
|
||||||
});
|
});
|
||||||
}, Cesium.ScreenSpaceEventType.RIGHT_UP);
|
}, Cesium.ScreenSpaceEventType.RIGHT_UP);
|
||||||
|
|
||||||
this._rightRestoreHandler = handler;
|
this._lookHandler = handler;
|
||||||
this.viewer.scene.postUpdate.addEventListener(() => this._clampLookAngles());
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// 右键拖拽期间,限制相对起始朝向的旋转/倾角
|
// 按累计角度(已限制)把相机绕点击点旋转;始终保持北朝上、看向点击点
|
||||||
_clampLookAngles() {
|
_orbitCamera(s) {
|
||||||
if (!this._rightRestore) return;
|
|
||||||
const camera = this.viewer.camera;
|
const camera = this.viewer.camera;
|
||||||
const s = this._rightRestore;
|
const sc = this._orbitScratch;
|
||||||
const MAX_ROT = (40 * Math.PI) / 180;
|
Cesium.Quaternion.fromAxisAngle(s.upAxis, s.azimuth, sc.qAz);
|
||||||
const MAX_TILT = (20 * Math.PI) / 180;
|
Cesium.Quaternion.fromAxisAngle(s.eastAxis, s.elevation, sc.qEl);
|
||||||
|
Cesium.Quaternion.multiply(sc.qEl, sc.qAz, sc.q);
|
||||||
let heading = camera.heading;
|
Cesium.Matrix3.fromQuaternion(sc.q, sc.rotMat);
|
||||||
let dh = heading - s.heading;
|
Cesium.Matrix3.multiplyByVector(sc.rotMat, s.startOffset, sc.newOffset);
|
||||||
while (dh > Math.PI) dh -= 2 * Math.PI;
|
Cesium.Cartesian3.add(s.pivot, sc.newOffset, sc.newPos);
|
||||||
while (dh < -Math.PI) dh += 2 * Math.PI;
|
const dir = Cesium.Cartesian3.normalize(
|
||||||
const rot = Math.max(-MAX_ROT, Math.min(MAX_ROT, dh));
|
Cesium.Cartesian3.subtract(s.pivot, sc.newPos, sc.dir),
|
||||||
|
sc.dir,
|
||||||
const pitch = camera.pitch;
|
);
|
||||||
const dp = pitch - s.pitch;
|
const z = dir.z;
|
||||||
const tilt = Math.max(-MAX_TILT, Math.min(MAX_TILT, dp));
|
sc.upProj.x = -dir.x * z;
|
||||||
|
sc.upProj.y = -dir.y * z;
|
||||||
if (rot !== dh || tilt !== dp) {
|
sc.upProj.z = 1 - z * z;
|
||||||
camera.setView({
|
Cesium.Cartesian3.normalize(sc.upProj, sc.upProj);
|
||||||
destination: camera.position,
|
camera.setView({
|
||||||
orientation: {
|
destination: Cesium.Cartesian3.clone(sc.newPos),
|
||||||
heading: s.heading + rot,
|
orientation: {
|
||||||
pitch: s.pitch + tilt,
|
direction: Cesium.Cartesian3.clone(dir),
|
||||||
roll: camera.roll,
|
up: Cesium.Cartesian3.clone(sc.upProj),
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// 相机平滑飞至某个标记上空并居中:回到与初始一致的高度与北朝上方向,左键拖拽手感不变
|
// 相机平滑飞至某个标记上空并居中:回到与初始一致的高度与北朝上方向,左键拖拽手感不变
|
||||||
|
|||||||
Reference in New Issue
Block a user