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.
@@ -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, _movement) {
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
- this._addDomEvent(canvas, 'mouseleave', this._onMouseLeave.bind(this));
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
- } else {
9648
- this._lastMouseData = { lng: '-', lat: '-', alt: '-' };
9788
+ this._updateContent();
9649
9789
  }
9650
-
9651
- this._updateContent();
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 camera = this._viewer.camera;
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
- return equatorCircumference / Math.pow(2, Math.max(0, level - 1));
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