UNPKG

5.12 kBJavaScriptView Raw
1"use strict";
2
3var _interopRequireWildcard = require("@babel/runtime/helpers/interopRequireWildcard");
4
5var _interopRequireDefault = require("@babel/runtime/helpers/interopRequireDefault");
6
7Object.defineProperty(exports, "__esModule", {
8 value: true
9});
10exports.default = useTouchMove;
11
12var _slicedToArray2 = _interopRequireDefault(require("@babel/runtime/helpers/slicedToArray"));
13
14var React = _interopRequireWildcard(require("react"));
15
16var MIN_SWIPE_DISTANCE = 0.1;
17var STOP_SWIPE_DISTANCE = 0.01;
18var REFRESH_INTERVAL = 20;
19var SPEED_OFF_MULTIPLE = Math.pow(0.995, REFRESH_INTERVAL); // ================================= Hook =================================
20
21function useTouchMove(ref, onOffset) {
22 var _useState = (0, React.useState)(),
23 _useState2 = (0, _slicedToArray2.default)(_useState, 2),
24 touchPosition = _useState2[0],
25 setTouchPosition = _useState2[1];
26
27 var _useState3 = (0, React.useState)(0),
28 _useState4 = (0, _slicedToArray2.default)(_useState3, 2),
29 lastTimestamp = _useState4[0],
30 setLastTimestamp = _useState4[1];
31
32 var _useState5 = (0, React.useState)(0),
33 _useState6 = (0, _slicedToArray2.default)(_useState5, 2),
34 lastTimeDiff = _useState6[0],
35 setLastTimeDiff = _useState6[1];
36
37 var _useState7 = (0, React.useState)(),
38 _useState8 = (0, _slicedToArray2.default)(_useState7, 2),
39 lastOffset = _useState8[0],
40 setLastOffset = _useState8[1];
41
42 var motionRef = (0, React.useRef)(); // ========================= Events =========================
43 // >>> Touch events
44
45 function onTouchStart(e) {
46 var _e$touches$ = e.touches[0],
47 screenX = _e$touches$.screenX,
48 screenY = _e$touches$.screenY;
49 setTouchPosition({
50 x: screenX,
51 y: screenY
52 });
53 window.clearInterval(motionRef.current);
54 }
55
56 function onTouchMove(e) {
57 if (!touchPosition) return;
58 e.preventDefault();
59 var _e$touches$2 = e.touches[0],
60 screenX = _e$touches$2.screenX,
61 screenY = _e$touches$2.screenY;
62 setTouchPosition({
63 x: screenX,
64 y: screenY
65 });
66 var offsetX = screenX - touchPosition.x;
67 var offsetY = screenY - touchPosition.y;
68 onOffset(offsetX, offsetY);
69 var now = Date.now();
70 setLastTimestamp(now);
71 setLastTimeDiff(now - lastTimestamp);
72 setLastOffset({
73 x: offsetX,
74 y: offsetY
75 });
76 }
77
78 function onTouchEnd() {
79 if (!touchPosition) return;
80 setTouchPosition(null);
81 setLastOffset(null); // Swipe if needed
82
83 if (lastOffset) {
84 var distanceX = lastOffset.x / lastTimeDiff;
85 var distanceY = lastOffset.y / lastTimeDiff;
86 var absX = Math.abs(distanceX);
87 var absY = Math.abs(distanceY); // Skip swipe if low distance
88
89 if (Math.max(absX, absY) < MIN_SWIPE_DISTANCE) return;
90 var currentX = distanceX;
91 var currentY = distanceY;
92 motionRef.current = window.setInterval(function () {
93 if (Math.abs(currentX) < STOP_SWIPE_DISTANCE && Math.abs(currentY) < STOP_SWIPE_DISTANCE) {
94 window.clearInterval(motionRef.current);
95 return;
96 }
97
98 currentX *= SPEED_OFF_MULTIPLE;
99 currentY *= SPEED_OFF_MULTIPLE;
100 onOffset(currentX * REFRESH_INTERVAL, currentY * REFRESH_INTERVAL);
101 }, REFRESH_INTERVAL);
102 }
103 } // >>> Wheel event
104
105
106 var lastWheelDirectionRef = (0, React.useRef)();
107
108 function onWheel(e) {
109 var deltaX = e.deltaX,
110 deltaY = e.deltaY; // Convert both to x & y since wheel only happened on PC
111
112 var mixed = 0;
113 var absX = Math.abs(deltaX);
114 var absY = Math.abs(deltaY);
115
116 if (absX === absY) {
117 mixed = lastWheelDirectionRef.current === 'x' ? deltaX : deltaY;
118 } else if (absX > absY) {
119 mixed = deltaX;
120 lastWheelDirectionRef.current = 'x';
121 } else {
122 mixed = deltaY;
123 lastWheelDirectionRef.current = 'y';
124 }
125
126 if (onOffset(-mixed, -mixed)) {
127 e.preventDefault();
128 }
129 } // ========================= Effect =========================
130
131
132 var touchEventsRef = (0, React.useRef)(null);
133 touchEventsRef.current = {
134 onTouchStart: onTouchStart,
135 onTouchMove: onTouchMove,
136 onTouchEnd: onTouchEnd,
137 onWheel: onWheel
138 };
139 React.useEffect(function () {
140 function onProxyTouchStart(e) {
141 touchEventsRef.current.onTouchStart(e);
142 }
143
144 function onProxyTouchMove(e) {
145 touchEventsRef.current.onTouchMove(e);
146 }
147
148 function onProxyTouchEnd(e) {
149 touchEventsRef.current.onTouchEnd(e);
150 }
151
152 function onProxyWheel(e) {
153 touchEventsRef.current.onWheel(e);
154 }
155
156 document.addEventListener('touchmove', onProxyTouchMove, {
157 passive: false
158 });
159 document.addEventListener('touchend', onProxyTouchEnd, {
160 passive: false
161 }); // No need to clean up since element removed
162
163 ref.current.addEventListener('touchstart', onProxyTouchStart, {
164 passive: false
165 });
166 ref.current.addEventListener('wheel', onProxyWheel);
167 return function () {
168 document.removeEventListener('touchmove', onProxyTouchMove);
169 document.removeEventListener('touchend', onProxyTouchEnd);
170 };
171 }, []);
172}
\No newline at end of file