pointPick.js 3.1 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768
  1. /**
  2. * 点位点选的 DOM 绑定(票 10):「左键点一下」与「左键拖着地图走」必须分开。
  3. *
  4. * 为什么必须有这道区分:票 03 的相机契约里左键是**平移**(抓着地图拖)。如果按下就打开点位摘要,
  5. * 用户每次拖完地图都会弹出一张卡片 —— 那不是新功能,是把已有交互弄坏。判据是**按下与松开的位移**:
  6. * 不超过阈值算点选,超过算拖拽。
  7. *
  8. * 本模块只**观察**事件,不 preventDefault、不 stopPropagation、不抢 pointer capture:
  9. * 相机的手感与三键契约完全不变(绑定在同一个元素上,两套监听互不干扰)。
  10. */
  11. /** 点选允许的最大位移(像素):手指/鼠标总会有几像素抖动,0 会把正常点选判成拖拽。 */
  12. export const CLICK_MAX_DRAG_PX = 4
  13. /**
  14. * 纯判据:两次坐标算不算「点一下」(脱浏览器可断言)。
  15. * @param {{x: number, y: number}} down 按下时的窗口坐标
  16. * @param {{x: number, y: number}} up 松开时的窗口坐标
  17. * @param {number} [maxDrag] 允许的最大位移
  18. */
  19. export function isClickGesture(down, up, maxDrag = CLICK_MAX_DRAG_PX) {
  20. if (!down || !up) return false
  21. const dx = Number(up.x) - Number(down.x)
  22. const dy = Number(up.y) - Number(down.y)
  23. if (!Number.isFinite(dx) || !Number.isFinite(dy)) return false
  24. return Math.hypot(dx, dy) <= maxDrag
  25. }
  26. /**
  27. * 绑定点选输入。
  28. * @param {Element} element 事件源(地图宿主:Cesium 画布的事件会冒泡到这里)
  29. * @param {(point: {x: number, y: number}) => void} onPick 判定为点选时回调(窗口坐标)
  30. * @param {object} [options] `{ maxDrag }`
  31. * @returns {() => void} detach
  32. */
  33. export function attachPointPick(element, onPick, options = {}) {
  34. // 与 `attachCameraInput` 同一道判据:不是可绑定元素就退回空 detach(脱浏览器调用不抛错)。
  35. if (!element || typeof element.addEventListener !== 'function') return () => {}
  36. const maxDrag = Number.isFinite(options.maxDrag) ? options.maxDrag : CLICK_MAX_DRAG_PX
  37. let down = null
  38. const onPointerDown = (event) => {
  39. // 只认左键:右键是轨道旋转(票 03),中键与其它键不参与点选。
  40. down = event.button === 0 ? { x: event.clientX, y: event.clientY, id: event.pointerId } : null
  41. }
  42. const onPointerUp = (event) => {
  43. const start = down
  44. down = null
  45. if (!start || event.pointerId !== start.id) return
  46. if (!isClickGesture(start, { x: event.clientX, y: event.clientY }, maxDrag)) return
  47. if (typeof onPick === 'function') onPick({ x: event.clientX, y: event.clientY })
  48. }
  49. const onPointerCancel = () => {
  50. down = null
  51. }
  52. element.addEventListener('pointerdown', onPointerDown)
  53. element.addEventListener('pointerup', onPointerUp)
  54. element.addEventListener('pointercancel', onPointerCancel)
  55. return () => {
  56. element.removeEventListener('pointerdown', onPointerDown)
  57. element.removeEventListener('pointerup', onPointerUp)
  58. element.removeEventListener('pointercancel', onPointerCancel)
  59. }
  60. }