UNPKG

@gdjiami/hooks

Version:

react hooks for mygzb.com

280 lines (240 loc) 7.61 kB
import { useRef, useCallback } from 'react'; import useInstance from './useInstance'; import useMultiTouchGesture from './useMultiTouchGesture'; import useRefProps from './useRefProps'; import useOnUnmount from './useOnUnmount'; export var SwipeDirection; (function (SwipeDirection) { SwipeDirection["Up"] = "up"; SwipeDirection["Down"] = "down"; SwipeDirection["Left"] = "left"; SwipeDirection["Right"] = "right"; })(SwipeDirection || (SwipeDirection = {})); /** * 获取向量的距离 */ function getLen(vector) { return Math.sqrt(Math.pow(vector.x, 2) + Math.pow(vector.y, 2)); } function dot(v1, v2) { return v1.x * v2.x + v1.y * v2.y; } function cross(v1, v2) { return v1.x * v2.y - v2.x * v1.y; } function getAngle(v1, v2) { var mr = getLen(v1) * getLen(v2); if (mr === 0) return 0; var r = dot(v1, v2) / mr; if (r > 1) r = 1; return Math.acos(r); } /** * 获取swipe的方向 */ function getSwipeDirection(from, to) { return Math.abs(from.x - to.x) > Math.abs(from.y - to.y) ? from.x > to.x // 水平 ? SwipeDirection.Left : SwipeDirection.Right : from.y > to.y ? SwipeDirection.Up : SwipeDirection.Down; } /** * 获取旋转的角度 */ function getRotateAngle(v1, v2) { var angle = getAngle(v1, v2); if (cross(v1, v2) > 0) { angle *= -1; } return angle * 180 / Math.PI; } export default function useTouch(options) { var _useInstance = useInstance(function () { return { isSingleTap: false, isDoubleTap: false }; }), state = _useInstance[0]; var optionRef = useRefProps(options); var ref = options.ref || useRef(); var cancelLongTap = useCallback(function () { clearTimeout(state.longTapTimeout); }, []); var cancelAfterLongTapTimeout = useCallback(function () { clearTimeout(state.afterLongTapTimeout); }, []); var cancelSingleTap = useCallback(function () { clearTimeout(state.singleTapTimeout); }, []); useOnUnmount(function () { cancelLongTap(); cancelAfterLongTapTimeout(); cancelSingleTap(); }); useMultiTouchGesture({ ref: ref, onDown: function onDown(info) { if (optionRef.current.onDown) { optionRef.current.onDown(info); } var now = state.start = Date.now(); var len = info.touches.length; var x1 = state.x1 = info.touches[0].pageX; var y1 = state.y1 = info.touches[0].pageY; var delta = now - (state.last || now); // 可能是双击: 时间间隔在250ms内, 且移动范围小于20 if (state.preTapPosition) { state.isDoubleTap = delta > 0 && delta <= 250 && Math.abs(state.preTapPosition.x - x1) < 30 && Math.abs(state.preTapPosition.y - y1) < 30; if (state.isDoubleTap) { cancelSingleTap(); } } state.preTapPosition = { x: x1, y: y1 }; state.last = now; if (len > 1) { // 多点触摸. 取消tap cancelLongTap(); cancelSingleTap(); // pinch 手势 var v = { x: info.touches[1].pageX - x1, y: info.touches[1].pageY - y1 }; state.preV = v; state.pinchStartLen = getLen(v); state.multiTouch = true; } else { state.isSingleTap = true; } // 长按事件 state.preventTap = false; if (!state.multiTouch) { state.longTapTimeout = window.setTimeout(function () { if (optionRef.current.onLongTap) { optionRef.current.onLongTap(info.touches[0]); } // 禁止长按之后tap事件 state.preventTap = true; }, 750); } }, onMove: function onMove(info) { if (optionRef.current.onMove) { optionRef.current.onMove(info); } var len = info.touches.length; var currentX = info.touches[0].pageX; var currentY = info.touches[0].pageY; var preV = state.preV; // 触摸点移动, 不再为tap state.isSingleTap = false; state.isDoubleTap = false; cancelLongTap(); if (len > 1) { var secCurrentX = info.touches[1].pageX; var secCurrentY = info.touches[1].pageY; var v = { x: secCurrentX - currentX, y: secCurrentY - currentY }; if (preV != null) { if (state.pinchStartLen && state.pinchStartLen > 0 && optionRef.current.onPinch) { // 触发的pinch事件 var center = { x: (secCurrentX + currentX) / 2, y: (secCurrentY + currentY) / 2 }; var scale = getLen(v) / state.pinchStartLen; optionRef.current.onPinch({ center: center, scale: scale }); } if (optionRef.current.onRotate) { // 触发rotate 事件 var angle = getRotateAngle(v, preV); optionRef.current.onRotate({ angle: angle }); } } state.preV = v; state.multiTouch = true; } else { // 移动事件 if (optionRef.current.onPressMove) { optionRef.current.onPressMove(info.touches[0]); } if (info.touches[0].distance > 15) { state.preventTap = true; } } // 记录最后移动的位置 state.x2 = currentX; state.y2 = currentY; }, onUp: function onUp(info) { if (optionRef.current.onUp) { optionRef.current.onUp(info); } cancelLongTap(); if (!state.multiTouch) { // 单点触摸 if (state.x2 && Math.abs(state.x1 - state.x2) > 30 || state.y2 && Math.abs(state.y1 - state.y2) > 30) { /** * swipe: 移动距离超过30px */ var direction = getSwipeDirection({ x: state.x1, y: state.y1 }, { x: state.x2, y: state.y2 }); var distance = direction === SwipeDirection.Left || direction === SwipeDirection.Right ? Math.abs(state.x1 - state.x2) : Math.abs(state.y1 - state.y2); if (optionRef.current.onSwipe) { optionRef.current.onSwipe({ direction: direction, distance: distance }); } } else { // long tap已经触发或者出现移动 if (state.preventTap) { state.preventTap = false; } else { if (optionRef.current.onTap) { /** * tap */ optionRef.current.onTap(info.changedTouches[0]); } if (state.isDoubleTap) { /** * double tap */ if (optionRef.current.onDoubleTap) { optionRef.current.onDoubleTap(info.changedTouches[0]); } cancelSingleTap(); state.isDoubleTap = false; } else if (state.isSingleTap) { /** * single tap: 如果250ms内 没有再发起点击, 则为单击 */ state.singleTapTimeout = window.setTimeout(function () { if (optionRef.current.onSingleTap) { optionRef.current.onSingleTap(info.changedTouches[0]); } }, 250); state.isSingleTap = false; } } } } state.preV = undefined; state.pinchStartLen = undefined; state.x1 = state.y1 = state.x2 = state.y2 = undefined; state.multiTouch = info.touches.length !== 0; } }); return { ref: ref }; }