UNPKG

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