use-swipe-to-dismiss
Version:
A simple React hook to dismiss an element by swiping
75 lines (74 loc) • 2.92 kB
JavaScript
import React, { useCallback, useEffect, useState } from 'react';
const defaultOptions = {
dismissThreshold: '50%',
snapbackDuration: 0.1,
snapbackEasing: 'ease-out',
};
/** Custom hook useSwipeToDismiss.js to manage long press. */
const useSwipeToDismiss = (options = {}) => {
// initialize default options
const { dismissThreshold, onDismiss, onDismissEnd, snapbackDuration, snapbackEasing } = Object.assign(Object.assign({}, defaultOptions), options);
// set dismiss threshold based on height
const [height, setHeight] = useState(0);
// track y position at start of drag
const [y0, setY0] = useState(0);
// track change in y position during drag
const [dy, setDY] = useState(0);
// enable animation during snapback
const [animate, setAnimate] = useState(false);
// calculate height on mount
const ref = React.createRef();
useEffect(() => {
if (!ref.current)
return;
setHeight(ref.current.offsetHeight);
}, []);
const start = useCallback((e) => {
var _a;
// do not animate during drag so that element tracks touch exactly
setAnimate(false);
setY0((_a = e.touches) === null || _a === void 0 ? void 0 : _a[0].pageY);
}, []);
const stop = useCallback((e) => {
// animate on release for snapback animation
setAnimate(true);
// check for dismiss threshold
// if specified as a percentage of height, use percentage of element height
const isPercentageOfHeight = typeof dismissThreshold === 'string' && dismissThreshold.endsWith('%');
const dismissThresholdPx = isPercentageOfHeight
? -parseFloat(dismissThreshold) * height / 100
: -parseFloat(dismissThreshold);
if (dy < dismissThresholdPx) {
setDY(-height * 2);
onDismiss === null || onDismiss === void 0 ? void 0 : onDismiss();
setTimeout(() => {
onDismissEnd === null || onDismissEnd === void 0 ? void 0 : onDismissEnd();
}, snapbackDuration * 1000);
}
else {
setDY(0);
}
}, [dy, height]);
const move = useCallback((e) => {
var _a;
// move element to track touch
const y = (_a = e.touches) === null || _a === void 0 ? void 0 : _a[0].pageY;
const dy = y - y0;
// resist dragging down by taking the square root of positive dy
const dyResistant = dy < 0 ? dy : Math.sqrt(dy);
setDY(dyResistant);
}, [y0]);
return {
onTouchStart: start,
onTouchEnd: stop,
onTouchMove: move,
onTouchCancel: stop,
ref,
style: {
transform: `translateY(${dy}px)`,
transition: animate ? `transform ${snapbackDuration}s ${snapbackEasing}` : '',
touchAction: 'none',
}
};
};
export default useSwipeToDismiss;