@yandex/ui
Version:
Yandex UI components
96 lines (95 loc) • 4.59 kB
JavaScript
;
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;