UNPKG

@jdcfe/yep-gesture

Version:

Support gesture for react component

577 lines (428 loc) 15.8 kB
"use strict"; var _interopRequireWildcard = require("@babel/runtime/helpers/interopRequireWildcard"); var _interopRequireDefault = require("@babel/runtime/helpers/interopRequireDefault"); Object.defineProperty(exports, "__esModule", { value: true }); exports["default"] = void 0; var _classCallCheck2 = _interopRequireDefault(require("@babel/runtime/helpers/classCallCheck")); var _createClass2 = _interopRequireDefault(require("@babel/runtime/helpers/createClass")); var _possibleConstructorReturn2 = _interopRequireDefault(require("@babel/runtime/helpers/possibleConstructorReturn")); var _getPrototypeOf2 = _interopRequireDefault(require("@babel/runtime/helpers/getPrototypeOf")); var _assertThisInitialized2 = _interopRequireDefault(require("@babel/runtime/helpers/assertThisInitialized")); var _inherits2 = _interopRequireDefault(require("@babel/runtime/helpers/inherits")); var _react = _interopRequireWildcard(require("react")); var _util = require("./util"); var _config = require("./config"); /* tslint:disable:no-console */ ; ; var directionMap = { all: _config.DIRECTION_ALL, vertical: _config.DIRECTION_VERTICAL, horizontal: _config.DIRECTION_HORIZONTAL }; var Gesture = /*#__PURE__*/ function (_Component) { (0, _inherits2["default"])(Gesture, _Component); function Gesture(props) { var _this; (0, _classCallCheck2["default"])(this, Gesture); _this = (0, _possibleConstructorReturn2["default"])(this, (0, _getPrototypeOf2["default"])(Gesture).call(this, props)); _this.state = {}; _this.triggerEvent = function (name) { var cb = _this.props[name]; if (typeof cb === 'function') { for (var _len = arguments.length, args = new Array(_len > 1 ? _len - 1 : 0), _key = 1; _key < _len; _key++) { args[_key - 1] = arguments[_key]; } // always give user gesture object as first params first cb.apply(void 0, [_this.getGestureState()].concat(args)); } }; _this.triggerCombineEvent = function (mainEventName, eventStatus) { var _this2, _this3; for (var _len2 = arguments.length, args = new Array(_len2 > 2 ? _len2 - 2 : 0), _key2 = 2; _key2 < _len2; _key2++) { args[_key2 - 2] = arguments[_key2]; } (_this2 = _this).triggerEvent.apply(_this2, [mainEventName].concat(args)); (_this3 = _this).triggerSubEvent.apply(_this3, [mainEventName, eventStatus].concat(args)); }; _this.triggerSubEvent = function (mainEventName, eventStatus) { if (eventStatus) { var _this4; var subEventName = (0, _util.getEventName)(mainEventName, eventStatus); for (var _len3 = arguments.length, args = new Array(_len3 > 2 ? _len3 - 2 : 0), _key3 = 2; _key3 < _len3; _key3++) { args[_key3 - 2] = arguments[_key3]; } (_this4 = _this).triggerEvent.apply(_this4, [subEventName].concat(args)); } }; _this.triggerPinchEvent = function (mainEventName, eventStatus) { var _this5; var scale = _this.gesture.scale; if (eventStatus === 'move' && typeof scale === 'number') { if (scale > 1) { _this.triggerEvent('onPinchOut'); } if (scale < 1) { _this.triggerEvent('onPinchIn'); } } for (var _len4 = arguments.length, args = new Array(_len4 > 2 ? _len4 - 2 : 0), _key4 = 2; _key4 < _len4; _key4++) { args[_key4 - 2] = arguments[_key4]; } (_this5 = _this).triggerCombineEvent.apply(_this5, [mainEventName, eventStatus].concat(args)); }; _this.initPressTimer = function () { _this.cleanPressTimer(); _this.pressTimer = setTimeout(function () { _this.setGestureState({ press: true }); _this.triggerEvent('onPress'); }, _config.PRESS.time); }; _this.cleanPressTimer = function () { /* tslint:disable:no-unused-expression */ _this.pressTimer && clearTimeout(_this.pressTimer); }; _this.setGestureState = function (params) { if (!_this.gesture) { _this.gesture = {}; } // cache the previous touches if (_this.gesture.touches) { _this.gesture.preTouches = _this.gesture.touches; } _this.gesture = Object.assign({}, _this.gesture, params); }; _this.getGestureState = function () { if (!_this.gesture) { return _this.gesture; } else { // shallow copy return Object.assign({}, _this.gesture); } }; _this.cleanGestureState = function () { delete _this.gesture; }; _this.getTouches = function (e) { return Array.prototype.slice.call(e.touches).map(function (item) { return { x: item.screenX, y: item.screenY }; }); }; _this.triggerUserCb = function (status, e) { var cbName = (0, _util.getEventName)('onTouch', status); if (cbName in _this.props) { _this.props[cbName](e); } }; _this._handleTouchStart = function (e) { _this.triggerUserCb('start', e); _this.event = e; if (e.touches.length > 1) { e.preventDefault(); } _this.initGestureStatus(e); _this.initPressTimer(); _this.checkIfMultiTouchStart(); }; _this.initGestureStatus = function (e) { _this.cleanGestureState(); // store the gesture start state var startTouches = _this.getTouches(e); var startTime = (0, _util.now)(); var startMutliFingerStatus = (0, _util.calcMutliFingerStatus)(startTouches); _this.setGestureState({ startTime: startTime, startTouches: startTouches, startMutliFingerStatus: startMutliFingerStatus, /* copy for next time touch move cala convenient*/ time: startTime, touches: startTouches, mutliFingerStatus: startMutliFingerStatus, srcEvent: _this.event }); }; _this.checkIfMultiTouchStart = function () { var _this$props = _this.props, enablePinch = _this$props.enablePinch, enableRotate = _this$props.enableRotate; var touches = _this.gesture.touches; if (touches.length > 1 && (enablePinch || enableRotate)) { if (enablePinch) { var startMutliFingerStatus = (0, _util.calcMutliFingerStatus)(touches); _this.setGestureState({ startMutliFingerStatus: startMutliFingerStatus, /* init pinch status */ pinch: true, scale: 1 }); _this.triggerCombineEvent('onPinch', 'start'); } if (enableRotate) { _this.setGestureState({ /* init rotate status */ rotate: true, rotation: 0 }); _this.triggerCombineEvent('onRotate', 'start'); } } }; _this._handleTouchMove = function (e) { _this.triggerUserCb('move', e); _this.event = e; if (!_this.gesture) { // sometimes weird happen: touchstart -> touchmove..touchmove.. --> touchend --> touchmove --> touchend // so we need to skip the unnormal event cycle after touchend return; } // not a long press _this.cleanPressTimer(); _this.updateGestureStatus(e); _this.checkIfSingleTouchMove(); _this.checkIfMultiTouchMove(); }; _this.checkIfMultiTouchMove = function () { var _this$gesture = _this.gesture, pinch = _this$gesture.pinch, rotate = _this$gesture.rotate, touches = _this$gesture.touches, startMutliFingerStatus = _this$gesture.startMutliFingerStatus, mutliFingerStatus = _this$gesture.mutliFingerStatus; if (!pinch && !rotate) { return; } if (touches.length < 2) { _this.setGestureState({ pinch: false, rotate: false }); // Todo: 2 finger -> 1 finger, wait to test this situation pinch && _this.triggerCombineEvent('onPinch', 'cancel'); rotate && _this.triggerCombineEvent('onRotate', 'cancel'); return; } if (pinch) { var scale = mutliFingerStatus.z / startMutliFingerStatus.z; _this.setGestureState({ scale: scale }); _this.triggerPinchEvent('onPinch', 'move'); } if (rotate) { var rotation = (0, _util.calcRotation)(startMutliFingerStatus, mutliFingerStatus); _this.setGestureState({ rotation: rotation }); _this.triggerCombineEvent('onRotate', 'move'); } }; _this.allowGesture = function () { return (0, _util.shouldTriggerDirection)(_this.gesture.direction, _this.directionSetting); }; _this.checkIfSingleTouchMove = function () { var _this$gesture2 = _this.gesture, pan = _this$gesture2.pan, touches = _this$gesture2.touches, moveStatus = _this$gesture2.moveStatus, preTouches = _this$gesture2.preTouches, _this$gesture2$availa = _this$gesture2.availablePan, availablePan = _this$gesture2$availa === void 0 ? true : _this$gesture2$availa; if (touches.length > 1) { _this.setGestureState({ pan: false }); // Todo: 1 finger -> 2 finger, wait to test this situation pan && _this.triggerCombineEvent('onPan', 'cancel'); return; } // add avilablePan condition to fix the case in scrolling, which will cause unavailable pan move. if (moveStatus && availablePan) { var direction = (0, _util.getMovingDirection)(preTouches[0], touches[0]); _this.setGestureState({ direction: direction }); var eventName = (0, _util.getDirectionEventName)(direction); if (!_this.allowGesture()) { // if the first move is unavailable, then judge all of remaining touch movings are also invalid. if (!pan) { _this.setGestureState({ availablePan: false }); } return; } if (!pan) { _this.triggerCombineEvent('onPan', 'start'); _this.setGestureState({ pan: true, availablePan: true }); } else { _this.triggerCombineEvent('onPan', eventName); _this.triggerSubEvent('onPan', 'move'); } } }; _this.checkIfMultiTouchEnd = function (status) { var _this$gesture3 = _this.gesture, pinch = _this$gesture3.pinch, rotate = _this$gesture3.rotate; if (pinch) { _this.triggerCombineEvent('onPinch', status); } if (rotate) { _this.triggerCombineEvent('onRotate', status); } }; _this.updateGestureStatus = function (e) { var time = (0, _util.now)(); _this.setGestureState({ time: time }); if (!e.touches || !e.touches.length) { return; } var _this$gesture4 = _this.gesture, startTime = _this$gesture4.startTime, startTouches = _this$gesture4.startTouches, pinch = _this$gesture4.pinch, rotate = _this$gesture4.rotate; var touches = _this.getTouches(e); var moveStatus = (0, _util.calcMoveStatus)(startTouches, touches, time - startTime); var mutliFingerStatus; if (pinch || rotate) { mutliFingerStatus = (0, _util.calcMutliFingerStatus)(touches); } _this.setGestureState({ /* update status snapshot */ touches: touches, mutliFingerStatus: mutliFingerStatus, /* update duration status */ moveStatus: moveStatus }); }; _this._handleTouchEnd = function (e) { _this.triggerUserCb('end', e); _this.event = e; if (!_this.gesture) { return; } _this.cleanPressTimer(); _this.updateGestureStatus(e); _this.doSingleTouchEnd('end'); _this.checkIfMultiTouchEnd('end'); }; _this._handleTouchCancel = function (e) { _this.triggerUserCb('cancel', e); _this.event = e; // Todo: wait to test cancel case if (!_this.gesture) { return; } _this.cleanPressTimer(); _this.updateGestureStatus(e); _this.doSingleTouchEnd('cancel'); _this.checkIfMultiTouchEnd('cancel'); }; _this.triggerAllowEvent = function (type, status) { if (_this.allowGesture()) { _this.triggerCombineEvent(type, status); } else { _this.triggerSubEvent(type, status); } }; _this.doSingleTouchEnd = function (status) { var _this$gesture5 = _this.gesture, moveStatus = _this$gesture5.moveStatus, pinch = _this$gesture5.pinch, rotate = _this$gesture5.rotate, press = _this$gesture5.press, pan = _this$gesture5.pan, startTouches = _this$gesture5.startTouches, touches = _this$gesture5.touches; if (pinch || rotate) { return; } if (moveStatus) { var z = moveStatus.z, velocity = moveStatus.velocity; var swipe = (0, _util.shouldTriggerSwipe)(z, velocity); _this.setGestureState({ swipe: swipe }); if (pan) { // pan need end, it's a process // sometimes, start with pan left, but end with pan right.... _this.triggerAllowEvent('onPan', status); } if (swipe) { //要计算最开始和最终触摸结束时的状态 var direction = (0, _util.getMovingDirection)(startTouches[0], touches[0]); //此处要更新direction,否则触发swipe事件时会使用错误的方向 _this.setGestureState({ direction: direction }); var directionEvName = (0, _util.getDirectionEventName)(direction); // swipe just need a direction, it's a endpoint _this.triggerAllowEvent('onSwipe', directionEvName); return; } } if (press) { _this.triggerEvent('onPressUp'); return; } _this.triggerEvent('onTap'); }; _this.getTouchAction = function () { var _this$props2 = _this.props, enablePinch = _this$props2.enablePinch, enableRotate = _this$props2.enableRotate; var _assertThisInitialize = (0, _assertThisInitialized2["default"])(_this), directionSetting = _assertThisInitialize.directionSetting; if (enablePinch || enableRotate || directionSetting === _config.DIRECTION_ALL) { return 'pan-x pan-y'; } if (directionSetting === _config.DIRECTION_VERTICAL) { return 'pan-x'; } if (directionSetting === _config.DIRECTION_HORIZONTAL) { return 'pan-y'; } return 'auto'; }; _this.directionSetting = directionMap[props.direction]; return _this; } (0, _createClass2["default"])(Gesture, [{ key: "componentWillUnmount", value: function componentWillUnmount() { this.cleanPressTimer(); } }, { key: "render", value: function render() { var children = this.props.children; var child = _react["default"].Children.only(children); var touchAction = this.getTouchAction(); var events = { onTouchStart: this._handleTouchStart, onTouchMove: this._handleTouchMove, onTouchCancel: this._handleTouchCancel, onTouchEnd: this._handleTouchEnd }; return _react["default"].cloneElement(child, Object.assign({}, events, { style: Object.assign({ touchAction: touchAction }, child.props.style || {}) })); } }]); return Gesture; }(_react.Component); exports["default"] = Gesture; Gesture.defaultProps = { enableRotate: false, enablePinch: false, direction: 'all' };