UNPKG

cross-gesture

Version:
364 lines 14.3 kB
import { getDirectionAndSpeed, pinchTimes, getRotateDeg } from './utils'; const MOVE_OUT_DISTANCE = 10; export class CrossGesture { constructor(el) { // 配置 this.options = { singleTapTimeout: 250, longTapTimeout: 750, }; // 事件监听回调函数 this.listenersMap = new Map(); // 之前滑动触点 this.touchPrePoints = {}; // 触摸点的滑动轨迹 this.touchPointsPath = new Map(); this.isMouseDown = false; this.onWheel = (evt) => { this.dispatch('wheel', evt); }; this.onPointerDown = (evt) => { this.dispatch('pointerdown', evt); }; this.onPointerUp = (evt) => { this.dispatch('pointerup', evt); }; this.onPointerMove = (evt) => { this.dispatch('pointermove', evt); }; this.onMouseDown = (evt) => { this.isMouseDown = true; // dispatch mousedown this.dispatch('mousedown', evt); // dispatch press move this.dispatchPressMove(evt); }; this.onMouseMove = (evt) => { // dispatch mousemove this.dispatch('mousemove', evt); if (!this.isMouseDown) { return; } // dispatch press move this.dispatchPressMove(evt); }; this.onMouseUp = (evt) => { this.isMouseDown = false; // dispatch mouseup this.dispatch('mouseup', evt); }; this.onTouchStart = (evt) => { // dispatch touchstart this.dispatch('touchstart', evt); // dispatch press move this.dispatchPressMove(evt); // update touch path this.updatePathOfPoint(evt.touches); // 清除上一个 single tap if (this.singleTapTimer) { clearTimeout(this.singleTapTimer); this.singleTapTimer = undefined; } if (!this.longTapTimer) { this.longTapPoint = evt.touches[0]; this.longTapTimer = setTimeout(() => { // dispatch longTap this.dispatch('longTap', evt); }, this.options.longTapTimeout); } }; this.onTouchMove = (evt) => { const touches = evt.touches; if (touches.length > 1) { evt.preventDefault(); } // dispatch touchmove this.dispatch('touchmove', evt); // dispatch press move this.dispatchPressMove(evt); // pinch const touchPrePoints = this.touchPrePoints.points || []; if (touches.length > 1 && touchPrePoints && touchPrePoints.length > 1) { const pre1 = touchPrePoints[0]; const pre2 = touchPrePoints[1]; const cur1 = touches[0]; const cur2 = touches[1]; const rect = this.element.getBoundingClientRect(); const fromPoints = [ { x: pre1.clientX - rect.x, y: pre1.clientY - rect.y, }, { x: pre2.clientX - rect.x, y: pre2.clientY - rect.y, }, ]; const toPoints = [ { x: cur1.clientX - rect.x, y: cur1.clientY - rect.y, }, { x: cur2.clientX - rect.x, y: cur2.clientY - rect.y, }, ]; const rotateDeg = getRotateDeg(fromPoints, toPoints); const zoomCenter = { x: (toPoints[0].x + toPoints[1].x) / 2, y: (toPoints[0].y + toPoints[1].y) / 2, }; const zoom = pinchTimes(fromPoints, toPoints); this.dispatch('pinch', evt, { fromPoints, toPoints, zoomCenter, zoom, }); this.dispatch('rotate', evt, { fromPoints, toPoints, rotateCenter: zoomCenter, rotateDeg, }); this.dispatch('pinchAndRotate', evt, { fromPoints, toPoints, rotateCenter: zoomCenter, rotateDeg, zoomCenter, zoom, }); } // swipe if (touchPrePoints.length > 0 && this.touchPrePoints.time) { const preTouch = touchPrePoints[0]; const touch = touches[0]; const fromPoint = { x: preTouch.clientX, y: preTouch.clientY, }; const toPoint = { x: touch.clientX, y: touch.clientY, }; this.dispatch('swipe', evt, getDirectionAndSpeed(fromPoint, toPoint, Date.now() - this.touchPrePoints.time)); } // update touch path this.updatePathOfPoint(evt.touches); for (let i = 0; i < evt.touches.length; i++) { const touch = evt.touches[i]; // cancel longTap if move out of distance if (this.longTapTimer && this.longTapPoint && this.longTapPoint.identifier === touch.identifier && this.moveOutDistance(touch.identifier)) { clearTimeout(this.longTapTimer); this.longTapTimer = undefined; this.longTapPoint = undefined; } } }; this.onTouchEnd = (evt) => { // dispatch touchend this.dispatch('touchend', evt); // evt.changedTouches 为结束触摸的点 if (!evt.changedTouches || !evt.changedTouches.length) { return; } for (let i = 0; i < evt.changedTouches.length; i++) { const touchEnd = evt.changedTouches[i]; // 清除 long tap if (this.longTapPoint && this.longTapPoint.identifier === touchEnd.identifier) { this.longTapTimer && clearTimeout(this.longTapTimer); this.longTapTimer = undefined; this.longTapPoint = undefined; } // double tap // singleTap event if (evt.changedTouches.length === 1 && evt.touches.length === 0 && !this.moveOutDistance(touchEnd.identifier)) { this.singleTapTimer = setTimeout(() => { this.singleTapTimer = undefined; this.dispatch('singleTap', evt); }, this.options.singleTapTimeout); } const now = Date.now(); // 滑动轨迹 const path = this.touchPointsPath.get(touchEnd.identifier); // tap event // 没有超过长安时间 // 滑动没超过 10 if (path && path[0] && now - path[0].time < this.options.longTapTimeout && !this.moveOutDistance(touchEnd.identifier)) { // dispatch tap event this.dispatch('tap', evt); // double tap event if (this.lastTapTime && now - this.lastTapTime < this.options.singleTapTimeout) { this.singleTapTimer && clearTimeout(this.singleTapTimer); this.singleTapTimer = undefined; this.lastTapTime = undefined; this.dispatch('doubleTap', evt); } else { this.lastTapTime = now; } } // 清除滑动轨迹 this.touchPointsPath.delete(touchEnd.identifier); } // clear touch points this.clearPrePoints(evt.changedTouches); }; this.onTouchCancel = (evt) => { var _a; // dispatch touchcancel this.dispatch('touchcancel', evt); // cancel longTap for (let i = 0; i < evt.touches.length; i++) { const touch = evt.touches[i]; if (((_a = this.longTapPoint) === null || _a === void 0 ? void 0 : _a.identifier) === touch.identifier) { this.longTapTimer && clearTimeout(this.longTapTimer); this.longTapTimer = undefined; this.longTapPoint = undefined; this.touchPointsPath.delete(touch.identifier); } } // clear touch points this.clearPrePoints(evt.changedTouches); }; const element = typeof el === 'string' ? document.querySelector(el) : el; if (!element) { throw new Error(`CrossGesture: Element ${el} does not exists on the document!`); } this.element = element; this.element.addEventListener('touchstart', this.onTouchStart); this.element.addEventListener('touchmove', this.onTouchMove, { passive: false }); this.element.addEventListener('touchend', this.onTouchEnd); this.element.addEventListener('touchcancel', this.onTouchCancel); this.element.addEventListener('touchstart', this.onTouchStart); this.element.addEventListener('mousedown', this.onMouseDown); this.element.addEventListener('mousemove', this.onMouseMove); this.element.addEventListener('mouseup', this.onMouseUp); this.element.addEventListener('pointerdown', this.onPointerDown); this.element.addEventListener('pointerup', this.onPointerUp); this.element.addEventListener('pointermove', this.onPointerMove); this.element.addEventListener('wheel', this.onWheel); } updatePathOfPoint(touches) { this.touchPrePoints = { points: Array.from(touches), time: Date.now(), }; for (let i = 0; i < touches.length; i++) { const touch = touches[i]; let path = this.touchPointsPath.get(touch.identifier); if (!path) { path = []; this.touchPointsPath.set(touch.identifier, path); } path.push({ time: Date.now(), pageX: touch.pageX, pageY: touch.pageY, }); } } dispatch(eventType, evt, detail) { const listeners = this.listenersMap.get(eventType); if (!listeners) { return; } listeners.forEach((l) => { l(evt, detail); }); } moveOutDistance(key, distance = MOVE_OUT_DISTANCE) { const path = this.touchPointsPath.get(key); if (!path) { return false; } const firstPoint = path[0]; for (let i = 1; i < path.length; i++) { const point = path[i]; if (point.pageX - firstPoint.pageX > distance || point.pageY - firstPoint.pageY > distance) { return true; } } return false; } clearPrePoints(touches) { if (!this.touchPrePoints.points || !this.touchPrePoints.points.length) { return; } const touchesMap = new Map(); for (let i = 0; i < touches.length; i++) { const touch = touches[i]; touchesMap.set(touch.identifier, touch); } this.touchPrePoints.points = this.touchPrePoints.points.filter((p) => !touchesMap.has(p.identifier)); } dispatchPressMove(evt) { const touch = evt; const rect = this.element.getBoundingClientRect(); if (touch.touches) { if (touch.touches.length !== 1) { return; } return this.dispatch('pressMove', touch, { point: { x: touch.touches[0].clientX - rect.x, y: touch.touches[0].clientY - rect.y, }, }); } const mouse = evt; return this.dispatch('pressMove', touch, { point: { x: mouse.clientX - rect.x, y: mouse.clientY - rect.y, }, }); } addListener(eventType, listener) { let listeners = this.listenersMap.get(eventType); if (!listeners) { listeners = []; this.listenersMap.set(eventType, listeners); } listeners.push(listener); } removeListener(eventType, listener) { const listeners = this.listenersMap.get(eventType); if (!listeners) { return; } // clear event timeout if (eventType === 'singleTap') { this.singleTapTimer && clearTimeout(this.singleTapTimer); this.singleTapTimer = undefined; } else if (eventType === 'longTap') { this.longTapTimer && clearTimeout(this.longTapTimer); this.longTapTimer = undefined; this.longTapPoint = undefined; } // clear listener this.listenersMap.set(eventType, listeners.filter((cb) => cb !== listener)); } destroy() { this.singleTapTimer && clearTimeout(this.singleTapTimer); this.longTapTimer && clearTimeout(this.longTapTimer); this.singleTapTimer = undefined; this.longTapTimer = undefined; this.longTapPoint = undefined; this.lastTapTime = undefined; this.touchPrePoints.points = undefined; this.touchPrePoints.time = undefined; this.touchPointsPath.clear(); this.listenersMap.clear(); } } //# sourceMappingURL=index.js.map