UNPKG

@yandex/ui

Version:

Yandex UI components

96 lines (95 loc) 4.59 kB
"use strict"; Object.defineProperty(exports, "__esModule", { value: true }); exports.useDrag = void 0; var react_1 = require("react"); var noop_1 = require("./noop"); /** * Опции для подписки на события */ var listenerOptions = { passive: false, capture: false, }; /** * Предоставляет унифицированный интерфейс для работы с * простыми тачевыми событиями (где используется один палец) */ var useDrag = function (elementRef, onStateChange) { var touchIdentifierRef = react_1.useRef(); var gestureStateRef = react_1.useRef(); var onStateChangeRef = react_1.useRef(); var handler = react_1.useCallback(function (event) { var onStateChange = onStateChangeRef.current || noop_1.noop; var state = gestureStateRef.current; var touch = Array.from(event.changedTouches).find(function (item) { return item.identifier === touchIdentifierRef.current; }); if (!state && event.type === 'touchstart' && event.changedTouches.length === 1) { touch = event.changedTouches[0]; touchIdentifierRef.current = touch.identifier; gestureStateRef.current = state = { first: true, last: false, startTime: event.timeStamp, initialPosition: { x: touch.clientX, y: touch.clientY }, data: {}, }; } if (state && touch) { // всегда обновляем ссылку на объект исходного события state.event = event; // сохраняет координаты предыдущего вызова функции if (event.type === 'touchmove') { state.first = false; state.previousPosition = state.currentPosition; } if (event.type === 'touchstart' || event.type === 'touchmove') { state.currentPosition = { x: touch.clientX, y: touch.clientY, }; state.movement = { x: state.currentPosition.x - state.initialPosition.x, y: state.currentPosition.y - state.initialPosition.y, }; state.delta = { x: state.currentPosition.x - (state.previousPosition || state.initialPosition).x, y: state.currentPosition.y - (state.previousPosition || state.initialPosition).y, }; state.velocity = { x: state.delta.x / (event.timeStamp - state.startTime - state.elapsedTime) || 0, y: state.delta.y / (event.timeStamp - state.startTime - state.elapsedTime) || 0, }; state.elapsedTime = event.timeStamp - state.startTime; } // жест завершен пользователем или был прекращен системой if (event.type === 'touchend' || event.type === 'touchcancel') { state.first = false; state.last = true; gestureStateRef.current = undefined; touchIdentifierRef.current = undefined; } onStateChange(state); } }, [onStateChangeRef]); // обновляем колбэк при каждом рендере, так нам не нужно будет использовать useCallback onStateChangeRef.current = onStateChange; /** * Управляет подписками на события */ react_1.useLayoutEffect(function () { if (!elementRef.current) return; var elem = elementRef.current; elem.addEventListener('touchstart', handler, listenerOptions); elem.addEventListener('touchmove', handler, listenerOptions); elem.addEventListener('touchend', handler, listenerOptions); elem.addEventListener('touchcancel', handler, listenerOptions); return function () { elem.removeEventListener('touchstart', handler, listenerOptions); elem.removeEventListener('touchmove', handler, listenerOptions); elem.removeEventListener('touchend', handler, listenerOptions); elem.removeEventListener('touchcancel', handler, listenerOptions); }; // eslint-disable-next-line react-hooks/exhaustive-deps }, [elementRef, handler, elementRef.current]); }; exports.useDrag = useDrag;