cesium-xs-sdk 1.0.16 → 1.0.18
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/cesium-xs-sdk.cjs.js +323 -17
- package/dist/cesium-xs-sdk.cjs.js.map +1 -1
- package/dist/cesium-xs-sdk.esm.js +323 -17
- package/dist/cesium-xs-sdk.esm.js.map +1 -1
- package/dist/cesium-xs-sdk.umd.js +1 -1
- package/dist/cesium-xs-sdk.umd.js.map +1 -1
- package/package.json +1 -1
- package/src/controls/LocationBar.js +7 -10
- package/src/controls/Zoom.js +177 -5
- package/src/index.d.ts +4 -0
- package/src/modules/EditorModule.js +6 -0
- package/src/modules/tools/BaseTool.js +19 -0
- package/src/modules/tools/DrawTool.js +43 -1
- package/src/modules/tools/MeasureTool.js +21 -1
- package/src/utils/DoubleClickDetector.js +51 -0
|
@@ -3566,6 +3566,58 @@ class EventModule {
|
|
|
3566
3566
|
}
|
|
3567
3567
|
}
|
|
3568
3568
|
|
|
3569
|
+
/**
|
|
3570
|
+
* 双击判定器
|
|
3571
|
+
*
|
|
3572
|
+
* 背景:Cesium 的 ScreenSpaceEventType.LEFT_DOUBLE_CLICK 唯一来源是 canvas 上的
|
|
3573
|
+
* DOM dblclick 事件,而 DOM dblclick 依赖浏览器/操作系统的双击合成
|
|
3574
|
+
* (时间阈值 + 位移阈值 + 触屏 touch slop + 双击缩放启发式),移动场景下经常
|
|
3575
|
+
* 不触发,导致"双击结束绘制/测量"在移动端静默失效。
|
|
3576
|
+
*
|
|
3577
|
+
* 本判定器在 SDK 自己的 LEFT_CLICK(mouse:click)流水上自维护双击判定:
|
|
3578
|
+
* 连续两次 click 间隔 < 时间阈值 且 屏幕位移 < 位移阈值,即判定为双击。
|
|
3579
|
+
* 如此双击收尾在鼠标、触屏、任何浏览器上行为一致,彻底移除对 DOM dblclick
|
|
3580
|
+
* 合成的依赖(桌面端 DOM dblclick 路径保留作为冗余,二者靠
|
|
3581
|
+
* _isDrawing / _isMeasuring 守卫天然防重)。
|
|
3582
|
+
*
|
|
3583
|
+
* 阈值取向:宁可漏判(用户多点一下),不可误判(把正常快速加点误判成双击收尾)。
|
|
3584
|
+
*/
|
|
3585
|
+
const DOUBLE_CLICK_INTERVAL_MS = 300; // 时间阈值(ms)
|
|
3586
|
+
const DOUBLE_CLICK_DISTANCE_PX = 10; // 屏幕位移阈值(px)
|
|
3587
|
+
|
|
3588
|
+
class DoubleClickDetector {
|
|
3589
|
+
constructor() {
|
|
3590
|
+
/** @type {{timestamp: number, screenPos: {x: number, y: number}} | null} */
|
|
3591
|
+
this._last = null;
|
|
3592
|
+
}
|
|
3593
|
+
|
|
3594
|
+
/**
|
|
3595
|
+
* 清空内部状态。工具 activate / deactivate / 绘制会话结束 / 取消时调用,
|
|
3596
|
+
* 避免跨工具、跨绘制会话的脏状态污染下一次判定。
|
|
3597
|
+
*/
|
|
3598
|
+
reset() {
|
|
3599
|
+
this._last = null;
|
|
3600
|
+
}
|
|
3601
|
+
|
|
3602
|
+
/**
|
|
3603
|
+
* 喂入一次 click,返回是否构成双击。
|
|
3604
|
+
* 注意:无论是否构成双击,本次点击都会成为下次判定的"上一次点击"。
|
|
3605
|
+
*
|
|
3606
|
+
* @param {number} timestamp performance.now()
|
|
3607
|
+
* @param {{x: number, y: number}} screenPos 屏幕坐标(取 movement.position)
|
|
3608
|
+
* @returns {boolean} 是否构成双击
|
|
3609
|
+
*/
|
|
3610
|
+
feed(timestamp, screenPos) {
|
|
3611
|
+
const prev = this._last;
|
|
3612
|
+
this._last = { timestamp, screenPos };
|
|
3613
|
+
if (!prev) return false;
|
|
3614
|
+
if (timestamp - prev.timestamp > DOUBLE_CLICK_INTERVAL_MS) return false;
|
|
3615
|
+
const dx = screenPos.x - prev.screenPos.x;
|
|
3616
|
+
const dy = screenPos.y - prev.screenPos.y;
|
|
3617
|
+
return Math.hypot(dx, dy) <= DOUBLE_CLICK_DISTANCE_PX;
|
|
3618
|
+
}
|
|
3619
|
+
}
|
|
3620
|
+
|
|
3569
3621
|
const Cesium$d = getCesium();
|
|
3570
3622
|
|
|
3571
3623
|
/**
|
|
@@ -3581,6 +3633,8 @@ class BaseTool {
|
|
|
3581
3633
|
this._options = options;
|
|
3582
3634
|
this._isActive = false;
|
|
3583
3635
|
this._handlers = []; // 注册的事件处理器 ID
|
|
3636
|
+
// 自维护双击判定器(DOM dblclick 在移动端经常不合成,见 DoubleClickDetector 注释)
|
|
3637
|
+
this._doubleClickDetector = new DoubleClickDetector();
|
|
3584
3638
|
}
|
|
3585
3639
|
|
|
3586
3640
|
/**
|
|
@@ -3589,6 +3643,8 @@ class BaseTool {
|
|
|
3589
3643
|
activate() {
|
|
3590
3644
|
if (this._isActive) return;
|
|
3591
3645
|
this._isActive = true;
|
|
3646
|
+
// 防止跨工具切换时残留上次会话的末次点击,污染本次双击判定
|
|
3647
|
+
this._doubleClickDetector.reset();
|
|
3592
3648
|
this.onActivate();
|
|
3593
3649
|
}
|
|
3594
3650
|
|
|
@@ -3598,6 +3654,7 @@ class BaseTool {
|
|
|
3598
3654
|
deactivate() {
|
|
3599
3655
|
if (!this._isActive) return;
|
|
3600
3656
|
this._isActive = false;
|
|
3657
|
+
this._doubleClickDetector.reset();
|
|
3601
3658
|
this.onDeactivate();
|
|
3602
3659
|
this.clearHandlers();
|
|
3603
3660
|
}
|
|
@@ -3612,6 +3669,19 @@ class BaseTool {
|
|
|
3612
3669
|
*/
|
|
3613
3670
|
onDeactivate() { }
|
|
3614
3671
|
|
|
3672
|
+
/**
|
|
3673
|
+
* 判定本次 mouse:click 是否构成"双击"(自维护判定,不依赖 DOM dblclick 合成)。
|
|
3674
|
+
* 子类应在"绘制 / 测量进行中"才调用本方法,避免把开启新会话的首次点击误判为双击。
|
|
3675
|
+
* @param {object} movement Cesium click 事件 movement(取 movement.position 作为屏幕坐标)
|
|
3676
|
+
* @returns {boolean}
|
|
3677
|
+
* @protected
|
|
3678
|
+
*/
|
|
3679
|
+
_isDoubleClick(movement) {
|
|
3680
|
+
const pos = movement && movement.position;
|
|
3681
|
+
if (!pos) return false;
|
|
3682
|
+
return this._doubleClickDetector.feed(performance.now(), { x: pos.x, y: pos.y });
|
|
3683
|
+
}
|
|
3684
|
+
|
|
3615
3685
|
/**
|
|
3616
3686
|
* 注册事件观察者
|
|
3617
3687
|
* @param {string} eventType 事件类型
|
|
@@ -3941,8 +4011,30 @@ class DrawTool extends BaseTool {
|
|
|
3941
4011
|
// lngLat 已经是经纬度数组 [lon, lat, height]
|
|
3942
4012
|
if (!lngLat) return;
|
|
3943
4013
|
|
|
3944
|
-
// 优先处理标绘类型
|
|
3945
4014
|
const plotConfig = PLOT_DRAW_CONFIG[this._drawType];
|
|
4015
|
+
|
|
4016
|
+
// === 自维护双击收尾判定 ===
|
|
4017
|
+
// DOM dblclick 在移动端/触屏经常不被浏览器合成(见 change-log),这里在
|
|
4018
|
+
// LEFT_CLICK 流水上自行判定:绘制中 + 类型支持双击收尾 + 两次 click
|
|
4019
|
+
// 时间/位移在阈值内 → 收尾。本次(双击第二次)点击不加点——双击第一次
|
|
4020
|
+
// 点击已把最后顶点加入 _tempPositions,直接收尾的结果与 DOM dblclick
|
|
4021
|
+
// 路径(_onDoubleClick pop 掉重叠点)逐点一致。
|
|
4022
|
+
// 不满足最小点数时降级为普通加点,落到下方正常逻辑。
|
|
4023
|
+
if (this._isDrawing && this._supportsDoubleClickFinish() && this._isDoubleClick(movement)) {
|
|
4024
|
+
const minPoints = plotConfig ? plotConfig.minPoints
|
|
4025
|
+
: (this._drawType === DrawType.POLYGON ? 3 : 2); // 线 2 / 面 3
|
|
4026
|
+
if (this._tempPositions.length >= minPoints) {
|
|
4027
|
+
if (plotConfig) {
|
|
4028
|
+
this._finishPlotDrawing(plotConfig);
|
|
4029
|
+
} else {
|
|
4030
|
+
this.finishDrawing();
|
|
4031
|
+
}
|
|
4032
|
+
return;
|
|
4033
|
+
}
|
|
4034
|
+
// 不满足最小点数:本次点击降级为普通加点(落到下方逻辑)
|
|
4035
|
+
}
|
|
4036
|
+
|
|
4037
|
+
// 优先处理标绘类型
|
|
3946
4038
|
if (plotConfig) {
|
|
3947
4039
|
this._handlePlotClick(lngLat, plotConfig);
|
|
3948
4040
|
return;
|
|
@@ -4082,6 +4174,18 @@ class DrawTool extends BaseTool {
|
|
|
4082
4174
|
}
|
|
4083
4175
|
}
|
|
4084
4176
|
|
|
4177
|
+
/**
|
|
4178
|
+
* 当前绘制类型是否支持"双击收尾":
|
|
4179
|
+
* 仅线 / 面 / 声明了 finishOnDoubleClick 的标绘类型支持;
|
|
4180
|
+
* 点 / 矩形 / 圆保持不响应(与 _onDoubleClick 的语义一致)。
|
|
4181
|
+
* @private
|
|
4182
|
+
*/
|
|
4183
|
+
_supportsDoubleClickFinish() {
|
|
4184
|
+
if (this._drawType === DrawType.LINE || this._drawType === DrawType.POLYGON) return true;
|
|
4185
|
+
const plotConfig = PLOT_DRAW_CONFIG[this._drawType];
|
|
4186
|
+
return !!(plotConfig && plotConfig.finishOnDoubleClick);
|
|
4187
|
+
}
|
|
4188
|
+
|
|
4085
4189
|
/**
|
|
4086
4190
|
* 双击结束绘制(移动端用:移动端没有右键;桌面端:避免绘制过程中双击误触发放大)
|
|
4087
4191
|
* 与 _onRightClick 等价,仅线 / 面 / 标绘(声明了 finishOnDoubleClick 的)响应;
|
|
@@ -4747,6 +4851,9 @@ class DrawTool extends BaseTool {
|
|
|
4747
4851
|
this._tempPositions = [];
|
|
4748
4852
|
this._previewPlot = null;
|
|
4749
4853
|
|
|
4854
|
+
// 绘制会话结束:清空双击判定器,避免下次绘制的开头点击与本次末次点击构成误判
|
|
4855
|
+
this._doubleClickDetector.reset();
|
|
4856
|
+
|
|
4750
4857
|
this._emitGraphicCreated(config.plotType, id);
|
|
4751
4858
|
}
|
|
4752
4859
|
|
|
@@ -4778,6 +4885,8 @@ class DrawTool extends BaseTool {
|
|
|
4778
4885
|
this._clickCount = 0;
|
|
4779
4886
|
this._startPos = null;
|
|
4780
4887
|
this._currentMousePos = null;
|
|
4888
|
+
// 绘制会话取消:清空双击判定器,避免下次绘制受本次末次点击污染
|
|
4889
|
+
this._doubleClickDetector.reset();
|
|
4781
4890
|
this._emitDrawCancel();
|
|
4782
4891
|
}
|
|
4783
4892
|
|
|
@@ -4833,6 +4942,9 @@ class DrawTool extends BaseTool {
|
|
|
4833
4942
|
this._clickCount = 0;
|
|
4834
4943
|
this._currentMousePos = null;
|
|
4835
4944
|
|
|
4945
|
+
// 绘制会话结束:清空双击判定器,避免下次绘制的开头点击与本次末次点击构成误判
|
|
4946
|
+
this._doubleClickDetector.reset();
|
|
4947
|
+
|
|
4836
4948
|
if (deferredPreviewLine) {
|
|
4837
4949
|
requestAnimationFrame(() => this.removeTempEntity(deferredPreviewLine));
|
|
4838
4950
|
}
|
|
@@ -7734,7 +7846,7 @@ class MeasureTool extends BaseTool {
|
|
|
7734
7846
|
controller.enableRotate = this._originalEnableRotate;
|
|
7735
7847
|
}
|
|
7736
7848
|
|
|
7737
|
-
_onClick(lngLat, _pickedEntity,
|
|
7849
|
+
_onClick(lngLat, _pickedEntity, movement) {
|
|
7738
7850
|
if (!lngLat) return;
|
|
7739
7851
|
|
|
7740
7852
|
// 拖拽刚结束:吞掉紧跟 mouseup 的 click,避免误开新测量
|
|
@@ -7743,6 +7855,21 @@ class MeasureTool extends BaseTool {
|
|
|
7743
7855
|
return;
|
|
7744
7856
|
}
|
|
7745
7857
|
|
|
7858
|
+
// === 自维护双击收尾判定 ===
|
|
7859
|
+
// DOM dblclick 在移动端/触屏经常不被浏览器合成(见 change-log),这里在
|
|
7860
|
+
// LEFT_CLICK 流水上自行判定:测量中(非拖拽中)+ 两次 click 时间/位移在
|
|
7861
|
+
// 阈值内 → 收尾。本次(双击第二次)点击不加点——双击第一次点击已把最后
|
|
7862
|
+
// 顶点加入 _positions,直接收尾的结果与 DOM dblclick 路径(_onDoubleClick
|
|
7863
|
+
// pop 掉重叠点)逐点一致。不满足最小点数时降级为普通加点。
|
|
7864
|
+
if (this._isMeasuring && !this._isDragging && this._isDoubleClick(movement)) {
|
|
7865
|
+
const minPoints = this._measureType === MeasureType.AREA ? 3 : 2;
|
|
7866
|
+
if (this._positions.length >= minPoints) {
|
|
7867
|
+
this._finishMeasurement();
|
|
7868
|
+
return;
|
|
7869
|
+
}
|
|
7870
|
+
// 不满足最小点数:本次点击降级为普通加点(落到下方逻辑)
|
|
7871
|
+
}
|
|
7872
|
+
|
|
7746
7873
|
// 同步鼠标位置为本次 click 的经纬度:修复"地图拖动后原地点击 → 预览线
|
|
7747
7874
|
// 自动连接到旧鼠标位置"的 bug(主要暴露在移动端双击完成测量后)。
|
|
7748
7875
|
//
|
|
@@ -7981,6 +8108,9 @@ class MeasureTool extends BaseTool {
|
|
|
7981
8108
|
// 测量已完成 → 恢复地图拖动 / 旋转
|
|
7982
8109
|
this._restoreCameraAfterMeasuring();
|
|
7983
8110
|
|
|
8111
|
+
// 测量会话结束:清空双击判定器,避免下次测量受本次末次点击污染
|
|
8112
|
+
this._doubleClickDetector.reset();
|
|
8113
|
+
|
|
7984
8114
|
this._emitMeasureComplete(result);
|
|
7985
8115
|
}
|
|
7986
8116
|
|
|
@@ -8082,6 +8212,8 @@ class MeasureTool extends BaseTool {
|
|
|
8082
8212
|
this.clearMeasurements();
|
|
8083
8213
|
// 取消时也要恢复地图拖动(与 _finishMeasurement 对称)
|
|
8084
8214
|
this._restoreCameraAfterMeasuring();
|
|
8215
|
+
// 测量会话取消:清空双击判定器,避免下次测量受本次末次点击污染
|
|
8216
|
+
this._doubleClickDetector.reset();
|
|
8085
8217
|
this._emitMeasureCancel();
|
|
8086
8218
|
}
|
|
8087
8219
|
|
|
@@ -8956,6 +9088,9 @@ class EditorModule {
|
|
|
8956
9088
|
this.currentToolName = name;
|
|
8957
9089
|
this.currentTool.activate();
|
|
8958
9090
|
this._isActive = true;
|
|
9091
|
+
// 供其他组件(如 Zoom 双击缩放)感知"编辑器工具激活中",
|
|
9092
|
+
// 工具激活期间双击/右键有工具自身语义,缩放等全局交互应让行
|
|
9093
|
+
this.viewer.xs3dEditorActive = true;
|
|
8959
9094
|
|
|
8960
9095
|
console.log(`工具 ${name} 已激活`);
|
|
8961
9096
|
return this;
|
|
@@ -8972,6 +9107,9 @@ class EditorModule {
|
|
|
8972
9107
|
this.currentTool = null;
|
|
8973
9108
|
this.currentToolName = null;
|
|
8974
9109
|
this._isActive = false;
|
|
9110
|
+
if (this.viewer) {
|
|
9111
|
+
this.viewer.xs3dEditorActive = false;
|
|
9112
|
+
}
|
|
8975
9113
|
|
|
8976
9114
|
return this;
|
|
8977
9115
|
}
|
|
@@ -9602,10 +9740,13 @@ class LocationBar extends BaseControl {
|
|
|
9602
9740
|
// 鼠标移动:更新鼠标位置信息
|
|
9603
9741
|
const canvas = this._viewer.scene.canvas;
|
|
9604
9742
|
this._addDomEvent(canvas, 'mousemove', this._onMouseMove.bind(this));
|
|
9605
|
-
|
|
9743
|
+
// 鼠标移出画布不再重置为 '-':保留最后拾取的坐标(见 _onMouseMove 说明)
|
|
9606
9744
|
|
|
9607
9745
|
// 相机变化:更新方向/俯仰/视高/层级
|
|
9608
9746
|
this._addCesiumHandler(this._viewer.camera.changed, this._onCameraChanged.bind(this));
|
|
9747
|
+
// flyTo 动画尾段每帧位移小于 percentageChanged 阈值后 changed 不再触发,
|
|
9748
|
+
// 信息栏会滞后一级;相机停止移动时强制刷新一次
|
|
9749
|
+
this._addCesiumHandler(this._viewer.camera.moveEnd, this._onCameraChanged.bind(this));
|
|
9609
9750
|
|
|
9610
9751
|
// FPS
|
|
9611
9752
|
if (this._showFPS) {
|
|
@@ -9644,16 +9785,10 @@ class LocationBar extends BaseControl {
|
|
|
9644
9785
|
lat: Cesium$6.Math.toDegrees(cartographic.latitude).toFixed(this._latDecimal),
|
|
9645
9786
|
alt: cartographic.height.toFixed(2)
|
|
9646
9787
|
};
|
|
9647
|
-
|
|
9648
|
-
this._lastMouseData = { lng: '-', lat: '-', alt: '-' };
|
|
9788
|
+
this._updateContent();
|
|
9649
9789
|
}
|
|
9650
|
-
|
|
9651
|
-
|
|
9652
|
-
}
|
|
9653
|
-
|
|
9654
|
-
_onMouseLeave() {
|
|
9655
|
-
this._lastMouseData = { lng: '-', lat: '-', alt: '-' };
|
|
9656
|
-
this._updateContent();
|
|
9790
|
+
// 拾取不到(鼠标指向太空/地球未加载等)时不刷新,
|
|
9791
|
+
// 保留最后一次成功拾取的坐标,避免信息栏闪现"-"
|
|
9657
9792
|
}
|
|
9658
9793
|
|
|
9659
9794
|
_onCameraChanged() {
|
|
@@ -9735,6 +9870,8 @@ class Zoom extends BaseControl {
|
|
|
9735
9870
|
/**
|
|
9736
9871
|
* @param {object} options
|
|
9737
9872
|
* @param {number} [options.duration=0.5] 缩放动画时长(秒)
|
|
9873
|
+
* @param {boolean} [options.doubleClickZoomIn=false] 左键双击放大一级(桌面端)
|
|
9874
|
+
* @param {boolean} [options.doubleClickZoomOut=false] 右键双击缩小一级(桌面端)
|
|
9738
9875
|
*/
|
|
9739
9876
|
constructor(options = {}) {
|
|
9740
9877
|
super({
|
|
@@ -9743,6 +9880,19 @@ class Zoom extends BaseControl {
|
|
|
9743
9880
|
});
|
|
9744
9881
|
|
|
9745
9882
|
this._duration = options.duration ?? 0.5;
|
|
9883
|
+
// 本控件发起的缩放飞行目标层级;飞行进行中(含动画尾段)点击按钮时,
|
|
9884
|
+
// 以它而不是相机瞬时位置作为基准层级
|
|
9885
|
+
this._pendingLevel = null;
|
|
9886
|
+
// 双击缩放(默认关闭;开启后会替换 Cesium Viewer 默认的左键双击动作)
|
|
9887
|
+
this._doubleClickZoomIn = options.doubleClickZoomIn ?? false;
|
|
9888
|
+
this._doubleClickZoomOut = options.doubleClickZoomOut ?? false;
|
|
9889
|
+
this._doubleClickEnabled = this._doubleClickZoomIn || this._doubleClickZoomOut;
|
|
9890
|
+
this._doubleClickInstalled = false;
|
|
9891
|
+
this._savedDoubleClickActions = null;
|
|
9892
|
+
// 右键双击判定器:Cesium 的 RIGHT_DOUBLE_CLICK 在桌面端没有任何触发源
|
|
9893
|
+
// (ScreenSpaceEventHandler 的 dblclick 监听只映射左键),
|
|
9894
|
+
// 因此右键双击在 RIGHT_CLICK 流水上自维护(复用工具的 DoubleClickDetector)
|
|
9895
|
+
this._rightDoubleClickDetector = new DoubleClickDetector();
|
|
9746
9896
|
}
|
|
9747
9897
|
|
|
9748
9898
|
_createContainer() {
|
|
@@ -9803,6 +9953,118 @@ class Zoom extends BaseControl {
|
|
|
9803
9953
|
e.stopPropagation();
|
|
9804
9954
|
this._zoomOut();
|
|
9805
9955
|
});
|
|
9956
|
+
|
|
9957
|
+
// 双击缩放注册到 viewer.screenSpaceEventHandler(而不是 DOM 事件):
|
|
9958
|
+
// 绘制/量测工具激活时会保存-移除-恢复当前的左键双击动作,
|
|
9959
|
+
// 注册在这里可以让工具自动接管,无需额外协调
|
|
9960
|
+
if (this._doubleClickEnabled) {
|
|
9961
|
+
this._installDoubleClickZoom();
|
|
9962
|
+
}
|
|
9963
|
+
}
|
|
9964
|
+
|
|
9965
|
+
/**
|
|
9966
|
+
* 开启双击缩放(运行期可随时调用,只切换内部开关,不重建事件)
|
|
9967
|
+
* 若初始化时未指定任何方向(两个选项均 false),默认只开左键双击放大
|
|
9968
|
+
* @returns {Zoom}
|
|
9969
|
+
*/
|
|
9970
|
+
enableDoubleClickZoom() {
|
|
9971
|
+
this._doubleClickEnabled = true;
|
|
9972
|
+
if (!this._doubleClickZoomIn && !this._doubleClickZoomOut) {
|
|
9973
|
+
this._doubleClickZoomIn = true;
|
|
9974
|
+
}
|
|
9975
|
+
// 初始化时未安装过(选项均为 false)则补注册;
|
|
9976
|
+
// 编辑器工具激活期间不抢占工具的左键双击,等工具结束恢复动作后即可用
|
|
9977
|
+
if (!this._doubleClickInstalled && this._viewer && !this._viewer.xs3dEditorActive) {
|
|
9978
|
+
this._installDoubleClickZoom();
|
|
9979
|
+
}
|
|
9980
|
+
return this;
|
|
9981
|
+
}
|
|
9982
|
+
|
|
9983
|
+
/**
|
|
9984
|
+
* 关闭双击缩放(运行期可随时调用;放大/缩小按钮不受影响)
|
|
9985
|
+
* @returns {Zoom}
|
|
9986
|
+
*/
|
|
9987
|
+
disableDoubleClickZoom() {
|
|
9988
|
+
this._doubleClickEnabled = false;
|
|
9989
|
+
return this;
|
|
9990
|
+
}
|
|
9991
|
+
|
|
9992
|
+
/**
|
|
9993
|
+
* 安装左键双击 / 右键双击缩放的输入动作,并保存被替换的原始动作供移除时恢复。
|
|
9994
|
+
* 左键走 Cesium 的 LEFT_DOUBLE_CLICK(桌面端 DOM dblclick);
|
|
9995
|
+
* 右键 Cesium 无 RIGHT_DOUBLE_CLICK 触发源,改在 RIGHT_CLICK 上用
|
|
9996
|
+
* DoubleClickDetector 自维护判定(连续两次右键间隔 < 300ms 且位移 < 10px)。
|
|
9997
|
+
*/
|
|
9998
|
+
_installDoubleClickZoom() {
|
|
9999
|
+
if (this._doubleClickInstalled || !this._viewer) return;
|
|
10000
|
+
const handler = this._viewer.screenSpaceEventHandler;
|
|
10001
|
+
if (!handler) return;
|
|
10002
|
+
|
|
10003
|
+
const EventType = Cesium$5.ScreenSpaceEventType;
|
|
10004
|
+
const saved = {};
|
|
10005
|
+
if (this._doubleClickZoomIn) {
|
|
10006
|
+
saved.left = handler.getInputAction(EventType.LEFT_DOUBLE_CLICK);
|
|
10007
|
+
handler.setInputAction(
|
|
10008
|
+
(movement) => this._onDoubleClickZoom(1, movement),
|
|
10009
|
+
EventType.LEFT_DOUBLE_CLICK
|
|
10010
|
+
);
|
|
10011
|
+
}
|
|
10012
|
+
if (this._doubleClickZoomOut) {
|
|
10013
|
+
saved.right = handler.getInputAction(EventType.RIGHT_CLICK);
|
|
10014
|
+
handler.setInputAction(
|
|
10015
|
+
(movement) => this._onRightClickForDoubleClick(movement),
|
|
10016
|
+
EventType.RIGHT_CLICK
|
|
10017
|
+
);
|
|
10018
|
+
}
|
|
10019
|
+
this._savedDoubleClickActions = saved;
|
|
10020
|
+
this._doubleClickInstalled = true;
|
|
10021
|
+
}
|
|
10022
|
+
|
|
10023
|
+
/**
|
|
10024
|
+
* 右键单击喂入双击判定器,构成双击时缩小一级
|
|
10025
|
+
* @param {object} movement Cesium 右键事件对象
|
|
10026
|
+
*/
|
|
10027
|
+
_onRightClickForDoubleClick(movement) {
|
|
10028
|
+
if (!this._doubleClickEnabled || !this._viewer) return;
|
|
10029
|
+
// 编辑器工具激活期间:右键是工具的撤销/结束语义,清状态不喂入,
|
|
10030
|
+
// 避免把两次撤销误判成双击缩放,或把脏状态带到工具结束后
|
|
10031
|
+
if (this._viewer.xs3dEditorActive) {
|
|
10032
|
+
this._rightDoubleClickDetector.reset();
|
|
10033
|
+
return;
|
|
10034
|
+
}
|
|
10035
|
+
const pos = movement?.position;
|
|
10036
|
+
if (!pos) return;
|
|
10037
|
+
if (this._rightDoubleClickDetector.feed(performance.now(), { x: pos.x, y: pos.y })) {
|
|
10038
|
+
this._rightDoubleClickDetector.reset();
|
|
10039
|
+
this._onDoubleClickZoom(-1, movement);
|
|
10040
|
+
}
|
|
10041
|
+
}
|
|
10042
|
+
|
|
10043
|
+
/**
|
|
10044
|
+
* 双击缩放处理:以双击拾取位置为中心步进一级
|
|
10045
|
+
* @param {number} levelDelta +1 放大一级,-1 缩小一级
|
|
10046
|
+
* @param {object} movement Cesium 双击事件对象
|
|
10047
|
+
*/
|
|
10048
|
+
_onDoubleClickZoom(levelDelta, movement) {
|
|
10049
|
+
if (!this._doubleClickEnabled || !this._viewer) return;
|
|
10050
|
+
// 绘制/量测/编辑工具激活期间禁用:左键双击是工具的收尾语义,
|
|
10051
|
+
// 右键单击是工具的撤销/结束语义,右键双击不能叠加缩放
|
|
10052
|
+
if (this._viewer.xs3dEditorActive) return;
|
|
10053
|
+
|
|
10054
|
+
const camera = this._viewer.camera;
|
|
10055
|
+
// 以双击位置拾取到的地表点为缩放中心;拾取不到退回相机正下方
|
|
10056
|
+
let cartographic = camera.positionCartographic;
|
|
10057
|
+
if (movement?.position && this._viewer.scene?.globe) {
|
|
10058
|
+
const ray = camera.getPickRay(movement.position);
|
|
10059
|
+
const cartesian = ray ? this._viewer.scene.globe.pick(ray, this._viewer.scene) : null;
|
|
10060
|
+
if (cartesian) {
|
|
10061
|
+
cartographic = Cesium$5.Cartographic.fromCartesian(cartesian);
|
|
10062
|
+
}
|
|
10063
|
+
}
|
|
10064
|
+
|
|
10065
|
+
const currentLevel = this._pendingLevel ?? this._getZoomLevel();
|
|
10066
|
+
const targetLevel = Math.max(0, Math.min(24, currentLevel + levelDelta));
|
|
10067
|
+
this._flyToLevel(targetLevel, cartographic);
|
|
9806
10068
|
}
|
|
9807
10069
|
|
|
9808
10070
|
_zoomIn() {
|
|
@@ -9818,12 +10080,21 @@ class Zoom extends BaseControl {
|
|
|
9818
10080
|
* @param {number} levelDelta 层级变化量,+1 放大一级,-1 缩小一级
|
|
9819
10081
|
*/
|
|
9820
10082
|
_zoomBy(levelDelta) {
|
|
9821
|
-
const
|
|
9822
|
-
const cartographic = camera.positionCartographic;
|
|
9823
|
-
const currentLevel = this._getZoomLevel();
|
|
10083
|
+
const currentLevel = this._pendingLevel ?? this._getZoomLevel();
|
|
9824
10084
|
const targetLevel = Math.max(0, Math.min(24, currentLevel + levelDelta));
|
|
10085
|
+
this._flyToLevel(targetLevel, this._viewer.camera.positionCartographic);
|
|
10086
|
+
}
|
|
10087
|
+
|
|
10088
|
+
/**
|
|
10089
|
+
* 飞到指定层级(以给定经纬度为中心),并记录 _pendingLevel
|
|
10090
|
+
* @param {number} targetLevel 目标层级
|
|
10091
|
+
* @param {Cesium.Cartographic} cartographic 目的地中心
|
|
10092
|
+
*/
|
|
10093
|
+
_flyToLevel(targetLevel, cartographic) {
|
|
10094
|
+
const camera = this._viewer.camera;
|
|
9825
10095
|
const targetHeight = this._getHeightByZoomLevel(targetLevel);
|
|
9826
10096
|
|
|
10097
|
+
this._pendingLevel = targetLevel;
|
|
9827
10098
|
camera.flyTo({
|
|
9828
10099
|
destination: Cesium$5.Cartesian3.fromRadians(
|
|
9829
10100
|
cartographic.longitude,
|
|
@@ -9835,7 +10106,13 @@ class Zoom extends BaseControl {
|
|
|
9835
10106
|
pitch: camera.pitch,
|
|
9836
10107
|
roll: camera.roll
|
|
9837
10108
|
},
|
|
9838
|
-
duration: this._duration
|
|
10109
|
+
duration: this._duration,
|
|
10110
|
+
complete: () => {
|
|
10111
|
+
if (this._pendingLevel === targetLevel) this._pendingLevel = null;
|
|
10112
|
+
},
|
|
10113
|
+
cancel: () => {
|
|
10114
|
+
if (this._pendingLevel === targetLevel) this._pendingLevel = null;
|
|
10115
|
+
}
|
|
9839
10116
|
});
|
|
9840
10117
|
}
|
|
9841
10118
|
|
|
@@ -9854,7 +10131,36 @@ class Zoom extends BaseControl {
|
|
|
9854
10131
|
|
|
9855
10132
|
_getHeightByZoomLevel(level) {
|
|
9856
10133
|
const equatorCircumference = 6378137 * 2 * Math.PI;
|
|
9857
|
-
|
|
10134
|
+
// 取层级几何带中心(C / 2^(L-0.5)),而不是分界线高度 C / 2^(L-1):
|
|
10135
|
+
// 分界线上浮点误差会导致层级反推差一级(实测约 1/3 经纬度位置),
|
|
10136
|
+
// 且动画尾段相机 changed 事件不再触发,信息栏会滞后一级显示。
|
|
10137
|
+
return equatorCircumference / Math.pow(2, Math.max(0, level - 0.5));
|
|
10138
|
+
}
|
|
10139
|
+
|
|
10140
|
+
/**
|
|
10141
|
+
* 销毁:恢复安装双击缩放前的输入动作
|
|
10142
|
+
* 编辑器工具激活期间不恢复(当前动作归工具所有,由工具的恢复链处理;
|
|
10143
|
+
* 工具结束后会放回本控件的 handler,其中 _viewer 已置空,调用即 no-op)
|
|
10144
|
+
*/
|
|
10145
|
+
_destroy() {
|
|
10146
|
+
if (this._doubleClickInstalled && this._viewer?.screenSpaceEventHandler) {
|
|
10147
|
+
const handler = this._viewer.screenSpaceEventHandler;
|
|
10148
|
+
const EventType = Cesium$5.ScreenSpaceEventType;
|
|
10149
|
+
const saved = this._savedDoubleClickActions || {};
|
|
10150
|
+
[
|
|
10151
|
+
['left', EventType.LEFT_DOUBLE_CLICK],
|
|
10152
|
+
['right', EventType.RIGHT_CLICK]
|
|
10153
|
+
].forEach(([key, eventType]) => {
|
|
10154
|
+
if (!(key in saved)) return;
|
|
10155
|
+
if (typeof saved[key] === 'function') {
|
|
10156
|
+
handler.setInputAction(saved[key], eventType);
|
|
10157
|
+
} else {
|
|
10158
|
+
handler.removeInputAction(eventType);
|
|
10159
|
+
}
|
|
10160
|
+
});
|
|
10161
|
+
}
|
|
10162
|
+
this._doubleClickInstalled = false;
|
|
10163
|
+
this._savedDoubleClickActions = null;
|
|
9858
10164
|
}
|
|
9859
10165
|
}
|
|
9860
10166
|
|