UNPKG

15.4 kBJavaScriptView Raw
1import _extends from 'babel-runtime/helpers/extends';
2import _classCallCheck from 'babel-runtime/helpers/classCallCheck';
3import _createClass from 'babel-runtime/helpers/createClass';
4import _possibleConstructorReturn from 'babel-runtime/helpers/possibleConstructorReturn';
5import _inherits from 'babel-runtime/helpers/inherits';
6/* tslint:disable:no-console */
7import React, { Component } from 'react';
8import { calcRotation, getEventName, now, calcMutliFingerStatus, calcMoveStatus, shouldTriggerSwipe, shouldTriggerDirection, getDirection, getDirectionEventName } from './util';
9import { PRESS, DIRECTION_ALL, DIRECTION_VERTICAL, DIRECTION_HORIZONTAL } from './config';
10;
11;
12var directionMap = {
13 all: DIRECTION_ALL,
14 vertical: DIRECTION_VERTICAL,
15 horizontal: DIRECTION_HORIZONTAL
16};
17
18var Gesture = function (_Component) {
19 _inherits(Gesture, _Component);
20
21 function Gesture(props) {
22 _classCallCheck(this, Gesture);
23
24 var _this = _possibleConstructorReturn(this, (Gesture.__proto__ || Object.getPrototypeOf(Gesture)).call(this, props));
25
26 _this.state = {};
27 _this.triggerEvent = function (name) {
28 for (var _len = arguments.length, args = Array(_len > 1 ? _len - 1 : 0), _key = 1; _key < _len; _key++) {
29 args[_key - 1] = arguments[_key];
30 }
31
32 var cb = _this.props[name];
33 if (typeof cb === 'function') {
34 // always give user gesture object as first params first
35 cb.apply(undefined, [_this.getGestureState()].concat(args));
36 }
37 };
38 _this.triggerCombineEvent = function (mainEventName, eventStatus) {
39 for (var _len2 = arguments.length, args = Array(_len2 > 2 ? _len2 - 2 : 0), _key2 = 2; _key2 < _len2; _key2++) {
40 args[_key2 - 2] = arguments[_key2];
41 }
42
43 _this.triggerEvent.apply(_this, [mainEventName].concat(args));
44 _this.triggerSubEvent.apply(_this, [mainEventName, eventStatus].concat(args));
45 };
46 _this.triggerSubEvent = function (mainEventName, eventStatus) {
47 for (var _len3 = arguments.length, args = Array(_len3 > 2 ? _len3 - 2 : 0), _key3 = 2; _key3 < _len3; _key3++) {
48 args[_key3 - 2] = arguments[_key3];
49 }
50
51 if (eventStatus) {
52 var subEventName = getEventName(mainEventName, eventStatus);
53 _this.triggerEvent.apply(_this, [subEventName].concat(args));
54 }
55 };
56 _this.triggerPinchEvent = function (mainEventName, eventStatus) {
57 for (var _len4 = arguments.length, args = Array(_len4 > 2 ? _len4 - 2 : 0), _key4 = 2; _key4 < _len4; _key4++) {
58 args[_key4 - 2] = arguments[_key4];
59 }
60
61 var scale = _this.gesture.scale;
62
63 if (eventStatus === 'move' && typeof scale === 'number') {
64 if (scale > 1) {
65 _this.triggerEvent('onPinchOut');
66 }
67 if (scale < 1) {
68 _this.triggerEvent('onPinchIn');
69 }
70 }
71 _this.triggerCombineEvent.apply(_this, [mainEventName, eventStatus].concat(args));
72 };
73 _this.initPressTimer = function () {
74 _this.cleanPressTimer();
75 _this.pressTimer = setTimeout(function () {
76 _this.setGestureState({
77 press: true
78 });
79 _this.triggerEvent('onPress');
80 }, PRESS.time);
81 };
82 _this.cleanPressTimer = function () {
83 /* tslint:disable:no-unused-expression */
84 _this.pressTimer && clearTimeout(_this.pressTimer);
85 };
86 _this.setGestureState = function (params) {
87 if (!_this.gesture) {
88 _this.gesture = {};
89 }
90 _this.gesture = _extends({}, _this.gesture, params);
91 };
92 _this.getGestureState = function () {
93 if (!_this.gesture) {
94 return _this.gesture;
95 } else {
96 // shallow copy
97 return _extends({}, _this.gesture);
98 }
99 };
100 _this.cleanGestureState = function () {
101 delete _this.gesture;
102 };
103 _this.getTouches = function (e) {
104 return Array.prototype.slice.call(e.touches).map(function (item) {
105 return {
106 x: item.pageX,
107 y: item.pageY
108 };
109 });
110 };
111 _this._handleTouchStart = function (e) {
112 _this.event = e;
113 if (e.touches.length > 1) {
114 e.preventDefault();
115 }
116 _this.initGestureStatus(e);
117 _this.initPressTimer();
118 _this.checkIfMultiTouchStart();
119 };
120 _this.initGestureStatus = function (e) {
121 _this.cleanGestureState();
122 // store the gesture start state
123 var startTouches = _this.getTouches(e);
124 var startTime = now();
125 var startMutliFingerStatus = calcMutliFingerStatus(startTouches);
126 _this.setGestureState({
127 startTime: startTime,
128 startTouches: startTouches,
129 startMutliFingerStatus: startMutliFingerStatus,
130 /* copy for next time touch move cala convenient*/
131 time: startTime,
132 touches: startTouches,
133 mutliFingerStatus: startMutliFingerStatus
134 });
135 };
136 _this.checkIfMultiTouchStart = function () {
137 var _this$props = _this.props,
138 enablePinch = _this$props.enablePinch,
139 enableRotate = _this$props.enableRotate;
140 var touches = _this.gesture.touches;
141
142 if (touches.length > 1 && (enablePinch || enableRotate)) {
143 if (enablePinch) {
144 var startMutliFingerStatus = calcMutliFingerStatus(touches);
145 _this.setGestureState({
146 startMutliFingerStatus: startMutliFingerStatus,
147 /* init pinch status */
148 pinch: true,
149 scale: 1
150 });
151 _this.triggerCombineEvent('onPinch', 'start');
152 }
153 if (enableRotate) {
154 _this.setGestureState({
155 /* init rotate status */
156 rotate: true,
157 rotation: 0
158 });
159 _this.triggerCombineEvent('onRotate', 'start');
160 }
161 }
162 };
163 _this._handleTouchMove = function (e) {
164 _this.event = e;
165 if (!_this.gesture) {
166 // sometimes weird happen: touchstart -> touchmove..touchmove.. --> touchend --> touchmove --> touchend
167 // so we need to skip the unnormal event cycle after touchend
168 return;
169 }
170 // not a long press
171 _this.cleanPressTimer();
172 _this.updateGestureStatus(e);
173 _this.checkIfSingleTouchMove();
174 _this.checkIfMultiTouchMove();
175 };
176 _this.checkIfMultiTouchMove = function () {
177 var _this$gesture = _this.gesture,
178 pinch = _this$gesture.pinch,
179 rotate = _this$gesture.rotate,
180 touches = _this$gesture.touches,
181 startMutliFingerStatus = _this$gesture.startMutliFingerStatus,
182 mutliFingerStatus = _this$gesture.mutliFingerStatus;
183
184 if (!pinch && !rotate) {
185 return;
186 }
187 if (touches.length < 2) {
188 _this.setGestureState({
189 pinch: false,
190 rotate: false
191 });
192 // Todo: 2 finger -> 1 finger, wait to test this situation
193 pinch && _this.triggerCombineEvent('onPinch', 'cancel');
194 rotate && _this.triggerCombineEvent('onRotate', 'cancel');
195 return;
196 }
197 if (pinch) {
198 var scale = mutliFingerStatus.z / startMutliFingerStatus.z;
199 _this.setGestureState({
200 scale: scale
201 });
202 _this.triggerPinchEvent('onPinch', 'move');
203 }
204 if (rotate) {
205 var rotation = calcRotation(startMutliFingerStatus, mutliFingerStatus);
206 _this.setGestureState({
207 rotation: rotation
208 });
209 _this.triggerCombineEvent('onRotate', 'move');
210 }
211 };
212 _this.allowGesture = function () {
213 return shouldTriggerDirection(_this.gesture.direction, _this.directionSetting);
214 };
215 _this.checkIfSingleTouchMove = function () {
216 var _this$gesture2 = _this.gesture,
217 pan = _this$gesture2.pan,
218 touches = _this$gesture2.touches,
219 moveStatus = _this$gesture2.moveStatus;
220
221 if (touches.length > 1) {
222 _this.setGestureState({
223 pan: false
224 });
225 // Todo: 1 finger -> 2 finger, wait to test this situation
226 pan && _this.triggerCombineEvent('onPan', 'cancel');
227 return;
228 }
229 if (moveStatus) {
230 var x = moveStatus.x,
231 y = moveStatus.y;
232
233 var direction = getDirection(x, y);
234 _this.setGestureState({
235 direction: direction
236 });
237 var eventName = getDirectionEventName(direction);
238 if (!_this.allowGesture()) {
239 return;
240 }
241 if (!pan) {
242 _this.triggerCombineEvent('onPan', 'start');
243 _this.setGestureState({
244 pan: true
245 });
246 } else {
247 _this.triggerCombineEvent('onPan', eventName);
248 _this.triggerSubEvent('onPan', 'move');
249 }
250 }
251 };
252 _this.checkIfMultiTouchEnd = function (status) {
253 var _this$gesture3 = _this.gesture,
254 pinch = _this$gesture3.pinch,
255 rotate = _this$gesture3.rotate;
256
257 if (pinch) {
258 _this.triggerCombineEvent('onPinch', status);
259 }
260 if (rotate) {
261 _this.triggerCombineEvent('onRotate', status);
262 }
263 };
264 _this.updateGestureStatus = function (e) {
265 var time = now();
266 _this.setGestureState({
267 time: time
268 });
269 if (!e.touches || !e.touches.length) {
270 return;
271 }
272 var _this$gesture4 = _this.gesture,
273 startTime = _this$gesture4.startTime,
274 startTouches = _this$gesture4.startTouches,
275 pinch = _this$gesture4.pinch,
276 rotate = _this$gesture4.rotate;
277
278 var touches = _this.getTouches(e);
279 var moveStatus = calcMoveStatus(startTouches, touches, time - startTime);
280 var mutliFingerStatus = void 0;
281 if (pinch || rotate) {
282 mutliFingerStatus = calcMutliFingerStatus(touches);
283 }
284 _this.setGestureState({
285 /* update status snapshot */
286 touches: touches,
287 mutliFingerStatus: mutliFingerStatus,
288 /* update duration status */
289 moveStatus: moveStatus
290 });
291 };
292 _this._handleTouchEnd = function (e) {
293 _this.event = e;
294 if (!_this.gesture) {
295 return;
296 }
297 _this.cleanPressTimer();
298 _this.updateGestureStatus(e);
299 _this.doSingleTouchEnd('end');
300 _this.checkIfMultiTouchEnd('end');
301 };
302 _this._handleTouchCancel = function (e) {
303 _this.event = e;
304 // Todo: wait to test cancel case
305 if (!_this.gesture) {
306 return;
307 }
308 _this.cleanPressTimer();
309 _this.updateGestureStatus(e);
310 _this.doSingleTouchEnd('cancel');
311 _this.checkIfMultiTouchEnd('cancel');
312 };
313 _this.doSingleTouchEnd = function (status) {
314 var _this$gesture5 = _this.gesture,
315 moveStatus = _this$gesture5.moveStatus,
316 pinch = _this$gesture5.pinch,
317 rotate = _this$gesture5.rotate,
318 press = _this$gesture5.press,
319 pan = _this$gesture5.pan,
320 direction = _this$gesture5.direction;
321
322 if (pinch || rotate || !_this.allowGesture()) {
323 return;
324 }
325 if (moveStatus) {
326 var z = moveStatus.z,
327 velocity = moveStatus.velocity;
328
329 var swipe = shouldTriggerSwipe(z, velocity);
330 _this.setGestureState({
331 swipe: swipe
332 });
333 if (pan) {
334 _this.triggerCombineEvent('onPan', status);
335 }
336 if (swipe) {
337 var directionEvName = getDirectionEventName(direction);
338 _this.triggerCombineEvent('onSwipe', directionEvName);
339 return;
340 }
341 }
342 if (press) {
343 _this.triggerEvent('onPressUp');
344 return;
345 }
346 _this.triggerEvent('onTap');
347 };
348 _this.getTouchAction = function () {
349 var _this$props2 = _this.props,
350 enablePinch = _this$props2.enablePinch,
351 enableRotate = _this$props2.enableRotate;
352 var directionSetting = _this.directionSetting;
353
354 if (enablePinch || enableRotate || directionSetting === DIRECTION_ALL) {
355 return 'pan-x pan-y';
356 }
357 if (directionSetting === DIRECTION_VERTICAL) {
358 return 'pan-x';
359 }
360 if (directionSetting === DIRECTION_HORIZONTAL) {
361 return 'pan-y';
362 }
363 return 'auto';
364 };
365 _this.directionSetting = directionMap[props.direction];
366 return _this;
367 }
368
369 _createClass(Gesture, [{
370 key: 'componentWillUnmount',
371 value: function componentWillUnmount() {
372 this.cleanPressTimer();
373 }
374 }, {
375 key: 'render',
376 value: function render() {
377 var children = this.props.children;
378
379 var child = React.Children.only(children);
380 var touchAction = this.getTouchAction();
381 var events = {
382 onTouchStart: this._handleTouchStart,
383 onTouchMove: this._handleTouchMove,
384 onTouchCancel: this._handleTouchCancel,
385 onTouchEnd: this._handleTouchEnd
386 };
387 return React.cloneElement(child, _extends({}, events, { style: {
388 touchAction: touchAction
389 } }));
390 }
391 }]);
392
393 return Gesture;
394}(Component);
395
396export default Gesture;
397
398Gesture.defaultProps = {
399 enableRotate: false,
400 enablePinch: false,
401 direction: 'all'
402};
\No newline at end of file