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