UNPKG

@past3lle/carousel-hooks

Version:

PASTELLE carousel animation hooks built on top of @usegesture and react-springs

541 lines (527 loc) 19.1 kB
'use strict'; Object.defineProperty(exports, '__esModule', { value: true }); function _interopDefault (ex) { return (ex && (typeof ex === 'object') && 'default' in ex) ? ex['default'] : ex; } var web = require('@react-spring/web'); var react = require('@use-gesture/react'); var clamp = _interopDefault(require('lodash.clamp')); var react$1 = require('react'); var hooks = require('@past3lle/hooks'); var utils$1 = require('@past3lle/utils'); function _extends() { return _extends = Object.assign ? Object.assign.bind() : function (n) { for (var e = 1; e < arguments.length; e++) { var t = arguments[e]; for (var r in t) ({}).hasOwnProperty.call(t, r) && (n[r] = t[r]); } return n; }, _extends.apply(null, arguments); } function _objectWithoutPropertiesLoose(r, e) { if (null == r) return {}; var t = {}; for (var n in r) if ({}.hasOwnProperty.call(r, n)) { if (e.includes(n)) continue; t[n] = r[n]; } return t; } var _excluded = ["dataLength", "itemSize", "axis", "dAxis", "visible", "prevRef"]; var _closerTo = function _closerTo(a, b, c) { return Math.abs(c - a) >= Math.abs(b - a) ? b : c; }; function _getLimits(point, mult) { var range = point / mult; var highPoint = mult * Math.ceil(range); var bounds = [highPoint - mult, highPoint]; return bounds; } function _calcAnchorPos(point, mult) { var _getLimits2 = _getLimits(point, mult), limitA = _getLimits2[0], limitB = _getLimits2[1]; return _closerTo(point, limitA, limitB); } function getNearestAxisPoint(point, multiple) { var anchorPoint = _calcAnchorPos(point, multiple); return anchorPoint; } var getIndex = function getIndex(axis, l) { return (axis < 0 ? axis + l : axis) % l; }; var getPos = function getPos(i, firstVisible, firstVisibleIndex, length) { return getIndex(i - firstVisible + firstVisibleIndex, length); }; var calculateInfiniteScrollApiLogic = function calculateInfiniteScrollApiLogic(i, axisDirection, _ref) { var _ref2; var prevRef = _ref.prevRef, active = _ref.active, last = _ref.last, axis = _ref.axis, dAxis = _ref.dAxis, mAxis = _ref.mAxis, firstVis = _ref.firstVis, firstVisIdx = _ref.firstVisIdx, scaleOptions = _ref.scaleOptions, snapOnScroll = _ref.snapOnScroll, config = _ref.config, dataLength = _ref.dataLength, itemSize = _ref.itemSize, setCurrentIndex = _ref.setCurrentIndex; var position = getPos(i, firstVis, firstVisIdx, dataLength); var prevPosition = getPos(i, prevRef.current[0], prevRef.current[1], dataLength); var rank = firstVis - (axis < 0 ? dataLength : 0) + position - firstVisIdx; var scale = mAxis && scaleOptions != null && scaleOptions.scaleOnScroll && active ? Math.max(1 - Math.abs(mAxis) / itemSize / 2, scaleOptions.scaleOnScroll) : scaleOptions.initialScale; var axisPos = -axis % (itemSize * dataLength) + itemSize * rank; var anchorPoint = last && getNearestAxisPoint(axisPos, itemSize); var onScreen = anchorPoint === 0; if (onScreen) { setCurrentIndex(i); } var configPos = dAxis > 0 ? position : dataLength - position; var immediate = dAxis < 0 ? prevPosition > position : prevPosition < position; return _ref2 = {}, _ref2[axisDirection] = !active && snapOnScroll ? anchorPoint || undefined : axisPos, _ref2.scale = scale, _ref2.immediate = immediate, _ref2.config = typeof config === 'function' ? config({ configPos: configPos, length: dataLength }) : config, _ref2; }; function runInfiniteScrollSprings(api, axisDirection, _ref3) { var dataLength = _ref3.dataLength, itemSize = _ref3.itemSize, axis = _ref3.axis, dAxis = _ref3.dAxis, visible = _ref3.visible, prevRef = _ref3.prevRef, rest = _objectWithoutPropertiesLoose(_ref3, _excluded); var itemPosition = Math.floor(axis / itemSize) % dataLength; var firstVis = getIndex(itemPosition, dataLength); var firstVisIdx = dAxis < 0 ? dataLength - visible - 1 : 1; api.start(function (i) { return calculateInfiniteScrollApiLogic(i, axisDirection, _extends({ axis: axis, dAxis: dAxis, firstVis: firstVis, firstVisIdx: firstVisIdx, itemSize: itemSize, dataLength: dataLength, prevRef: prevRef }, rest)); }); prevRef.current = [firstVis, firstVisIdx]; } var runLimitedSwipe = function runLimitedSwipe(_ref4, _ref5) { var api = _ref4[1]; var axisDirection = _ref5.axis, indexOptions = _ref5.indexOptions, itemSize = _ref5.itemSize; return function (_ref6) { var active = _ref6.active, movement = _ref6.movement, direction = _ref6.direction, cancel = _ref6.cancel; var axis = axisDirection === 'x' ? 0 : 1; var _ref7 = [movement[axis], direction[axis]], mAxis = _ref7[0], gestDir = _ref7[1]; if (gestDir) { var current = indexOptions.current, lastIndx = indexOptions.last, setIndex = indexOptions.setIndex; var bounds = [current.current - 1 > 0 ? current.current - 1 : 0, current.current + 1 < lastIndx ? current.current + 1 : lastIndx]; if (active && Math.abs(mAxis) > itemSize / 10) { var clampedIdx = clamp.apply(void 0, [current.current + -gestDir].concat(bounds)); current.current = clampedIdx; cancel == null || cancel(); setIndex == null || setIndex(clampedIdx); } api.start(function (i) { var _ref8; if (i < current.current - 1 || i > current.current + 1) return { display: 'none' }; var axisPoint = (i - current.current) * itemSize + (active ? mAxis : 0); return _ref8 = {}, _ref8[axisDirection] = axisPoint, _ref8.display = 'block', _ref8; }); } }; }; var runPinchZoom = function runPinchZoom(_ref9, _ref10) { var springs = _ref9[0], api = _ref9[1]; var ref = _ref10.ref; return function (_ref11) { var _ref11$args = _ref11.args, index = _ref11$args[0], _ref11$origin = _ref11.origin, ox = _ref11$origin[0], oy = _ref11$origin[1], first = _ref11.first, _ref11$movement = _ref11.movement, ms = _ref11$movement[0], _ref11$offset = _ref11.offset, s = _ref11$offset[0], memo = _ref11.memo; if (first) { memo = {}; var refSizes = ref == null ? void 0 : ref.getBoundingClientRect(); var tx = ox - (ox + ((refSizes == null ? void 0 : refSizes.width) || 0) / 2); var ty = oy - (oy + ((refSizes == null ? void 0 : refSizes.height) || 0) / 2); memo[index.toString()] = [springs[index].x.get(), springs[index].y.get(), tx, ty]; } var x = memo[index.toString()][0] - (ms - 1) * memo[index.toString()][2]; var y = memo[index.toString()][1] - (ms - 1) * memo[index.toString()][3]; api.start(function () { return { scale: s, x: x, y: y }; }); return memo; }; }; var utils = { wheel: { infinite: runInfiniteScrollSprings }, drag: { infinite: runInfiniteScrollSprings, limited: runLimitedSwipe }, pinch: { zoom: runPinchZoom } }; function useScrollZoneRefs(axisDirection, sizeOptions, auxOptions) { if ((sizeOptions == null ? void 0 : sizeOptions.minSize) === 0) utils$1.devWarn('[ScrollRef] Setup warning! Size 0 (ZERO) minSize passed. This could cause layout issues! Check the options object passed to your useScroll animation hooks.'); var windowSizes = hooks.useWindowSize(auxOptions == null ? void 0 : auxOptions.windowSizeOptions); var _useStateRef = hooks.useStateRef(null, function (node) { return node; }), scrollingZoneTarget = _useStateRef[0], setScrollingZoneRef = _useStateRef[1]; var isVertical = axisDirection === 'y'; var _useStateRef2 = hooks.useStateRef(0, function (node) { return isVertical ? node == null ? void 0 : node.clientHeight : node == null ? void 0 : node.clientWidth; }), nodeSize = _useStateRef2[0], setItemSizeRef = _useStateRef2[1]; var itemSize = (sizeOptions == null ? void 0 : sizeOptions.fixedSize) || (sizeOptions == null ? void 0 : sizeOptions.minSize) && Math.min(sizeOptions.minSize, nodeSize) || nodeSize; react$1.useEffect(function () { var handler = function handler(e) { return e.preventDefault(); }; scrollingZoneTarget == null || scrollingZoneTarget.addEventListener('gesturestart', handler); scrollingZoneTarget == null || scrollingZoneTarget.addEventListener('gesturechange', handler); scrollingZoneTarget == null || scrollingZoneTarget.addEventListener('gestureend', handler); return function () { scrollingZoneTarget == null || scrollingZoneTarget.removeEventListener('gesturestart', handler); scrollingZoneTarget == null || scrollingZoneTarget.removeEventListener('gesturechange', handler); scrollingZoneTarget == null || scrollingZoneTarget.removeEventListener('gestureend', handler); }; }, [scrollingZoneTarget]); react$1.useEffect(function () { if (!(sizeOptions != null && sizeOptions.fixedSize) && (isVertical ? scrollingZoneTarget == null ? void 0 : scrollingZoneTarget.clientHeight : scrollingZoneTarget == null ? void 0 : scrollingZoneTarget.clientWidth)) { setItemSizeRef(scrollingZoneTarget); } }, [sizeOptions, setItemSizeRef, windowSizes, scrollingZoneTarget, axisDirection, isVertical]); return { refs: { scrollingZoneTarget: scrollingZoneTarget, itemSize: itemSize }, refCallbacks: { setScrollingZoneRef: setScrollingZoneRef, setItemSizeRef: setItemSizeRef } }; } function useInfiniteScrollSetup(axisDirection, options, auxOptions) { var _useState = react$1.useState(false), firstAnimationOver = _useState[0], setFirstPaintOver = _useState[1]; var prevRef = react$1.useRef([0, 1]); var _useState2 = react$1.useState(prevRef.current[0]), currentIndex = _useState2[0], setCurrentIndex = _useState2[1]; var _useScrollZoneRefs = useScrollZoneRefs(axisDirection, options.sizeOptions, auxOptions), _useScrollZoneRefs$re = _useScrollZoneRefs.refs, itemSize = _useScrollZoneRefs$re.itemSize, scrollingZoneTarget = _useScrollZoneRefs$re.scrollingZoneTarget, _useScrollZoneRefs$re2 = _useScrollZoneRefs.refCallbacks, setItemSizeRef = _useScrollZoneRefs$re2.setItemSizeRef, setScrollingZoneRef = _useScrollZoneRefs$re2.setScrollingZoneRef; var gestureParams = react$1.useMemo(function () { return _extends({}, options, { prevRef: prevRef, itemSize: itemSize, setCurrentIndex: setCurrentIndex }); }, [itemSize, options]); return { gestureParams: gestureParams, currentIndex: currentIndex, firstAnimationOver: firstAnimationOver, scrollingZoneTarget: scrollingZoneTarget, callbacks: { setFirstPaintOver: setFirstPaintOver, setScrollingZoneRef: setScrollingZoneRef, setItemSizeRef: setItemSizeRef } }; } var _excluded$1 = ["setFirstPaintOver"]; var DRAG_SPEED_COEFFICIENT = 0.5; function useInfiniteHorizontalScroll(items, options) { var _useInfiniteScrollSet = useInfiniteScrollSetup('x', options), gestureParams = _useInfiniteScrollSet.gestureParams, currentIndex = _useInfiniteScrollSet.currentIndex, firstAnimationOver = _useInfiniteScrollSet.firstAnimationOver, scrollingZoneTarget = _useInfiniteScrollSet.scrollingZoneTarget, _useInfiniteScrollSet2 = _useInfiniteScrollSet.callbacks, setFirstPaintOver = _useInfiniteScrollSet2.setFirstPaintOver, restCbs = _objectWithoutPropertiesLoose(_useInfiniteScrollSet2, _excluded$1); var _useSprings = web.useSprings(items.length, function (i) { return _extends({}, options == null ? void 0 : options.styleMixin, { x: (i < items.length - 1 ? i : -1) * gestureParams.itemSize, onRest: function onRest() { if (!firstAnimationOver) { return setFirstPaintOver(true); } }, from: { x: i * gestureParams.itemSize }, config: { tension: 260, friction: 50 } }); }), springs = _useSprings[0], api = _useSprings[1]; var bind = react.useDrag(function (_ref) { var active = _ref.active, dragging = _ref.dragging, _ref$offset = _ref.offset, x = _ref$offset[0], _ref$direction = _ref.direction, dx = _ref$direction[0]; runInfiniteScrollSprings(api, 'x', _extends({}, gestureParams, { dataLength: items.length, axis: -x / (options.scrollSpeed || DRAG_SPEED_COEFFICIENT), dAxis: -dx, active: !!(active || dragging), config: options.config })); }, { eventOptions: { passive: true, capture: false, once: true }, preventDefault: true, axis: 'x', filterTaps: true }); return { target: scrollingZoneTarget, bind: bind, springs: springs, state: { currentIndex: currentIndex, itemSize: gestureParams.itemSize, firstAnimationOver: firstAnimationOver }, refCallbacks: restCbs }; } var STIFF_SPRINGS = { tension: 260, friction: 50 }; var _excluded$2 = ["setFirstPaintOver"]; var CONFIG = { SCROLL_SPEED_COEFFICIENT: 3.2, DRAG_SPEED_COEFFICIENT: 0.5 }; function useInfiniteVerticalScroll(items, options) { var isMobile = hooks.useIsMobile(); var _useInfiniteScrollSet = useInfiniteScrollSetup('y', options), gestureParams = _useInfiniteScrollSet.gestureParams, currentIndex = _useInfiniteScrollSet.currentIndex, firstAnimationOver = _useInfiniteScrollSet.firstAnimationOver, _useInfiniteScrollSet2 = _useInfiniteScrollSet.callbacks, setFirstPaintOver = _useInfiniteScrollSet2.setFirstPaintOver, restCbs = _objectWithoutPropertiesLoose(_useInfiniteScrollSet2, _excluded$2); var lastIndex = items.length - 1; var getDefaultPosition = react$1.useCallback(function (i) { return _extends({}, options == null ? void 0 : options.styleMixin, { scale: options.scaleOptions.initialScale || 0.92, y: (i < lastIndex ? i : -1) * gestureParams.itemSize, onRest: function onRest() { if (!firstAnimationOver) { setFirstPaintOver(true); } }, config: STIFF_SPRINGS }); }, [lastIndex, gestureParams.itemSize, firstAnimationOver]); var gestureApi = web.useSprings(items.length, getDefaultPosition, [gestureParams.itemSize]); var wheelOffset = react$1.useRef(0); var dragOffset = react$1.useRef(0); var dragIndexRef = react$1.useRef(0); var bind = react.useGesture({ onDrag: utils.drag.limited(gestureApi, { axis: 'y', itemSize: gestureParams.itemSize, indexOptions: { current: dragIndexRef, setIndex: undefined, last: lastIndex } }), onWheel: function onWheel(_ref) { var active = _ref.active, last = _ref.last, _ref$offset = _ref.offset, y = _ref$offset[1], _ref$movement = _ref.movement, my = _ref$movement[1], _ref$direction = _ref.direction, dy = _ref$direction[1]; if (dy) { wheelOffset.current = y; var computedY = dragOffset.current + y / CONFIG.SCROLL_SPEED_COEFFICIENT; runInfiniteScrollSprings(gestureApi[1], 'y', _extends({}, gestureParams, { dataLength: items.length, active: active, axis: computedY, dAxis: dy, mAxis: my, last: last })); } } }, { drag: { axis: 'y', preventScrollAxis: 'x' } }); return { bind: bind, springs: gestureApi[0], state: { currentIndex: isMobile ? 0 : currentIndex, itemSize: gestureParams.itemSize, firstAnimationOver: firstAnimationOver }, refCallbacks: restCbs }; } function useLimitedSwipe(axis, data, options, auxOptions) { var _useScrollZoneRefs = useScrollZoneRefs(axis, options == null ? void 0 : options.sizeOptions, auxOptions), itemSize = _useScrollZoneRefs.refs.itemSize, refCallbacks = _useScrollZoneRefs.refCallbacks; var indexRef = react$1.useRef(0); var _useState = react$1.useState(indexRef.current), indexState = _useState[0], setIndexState = _useState[1]; var _useSprings = web.useSprings(data.length, function (i) { var _extends2; return _extends({}, options == null ? void 0 : options.styleMixin, (_extends2 = {}, _extends2[axis] = i * itemSize, _extends2.display = 'block', _extends2)); }, [itemSize]), springs = _useSprings[0], api = _useSprings[1]; var bind = react.useDrag(utils.drag.limited([, api], { axis: axis, indexOptions: { current: indexRef, setIndex: setIndexState, last: data.length - 1 }, itemSize: itemSize }), { axis: axis }); return { bind: bind, springs: springs, state: { currentIndex: indexState, itemSize: itemSize }, refCallbacks: refCallbacks }; } function useLimitedHorizontalSwipe(data, options, auxOptions) { return useLimitedSwipe('x', data, options, auxOptions); } function useLimitedVerticalSwipe(data, options, auxOptions) { return useLimitedSwipe('y', data, options, auxOptions); } function usePinchZoomAndDrag(data, options, auxOptions) { var _useScrollZoneRefs = useScrollZoneRefs('x', options == null ? void 0 : options.sizeOptions, auxOptions), _useScrollZoneRefs$re = _useScrollZoneRefs.refs, itemSize = _useScrollZoneRefs$re.itemSize, ref = _useScrollZoneRefs$re.scrollingZoneTarget, refCallbacks = _useScrollZoneRefs.refCallbacks; var _useSprings = web.useSprings(data.length, function (i) { return _extends({}, options == null ? void 0 : options.styleMixin, { x: i * itemSize, y: 0, scale: 1, display: 'block' }); }, [data.length]), springs = _useSprings[0], api = _useSprings[1]; var bind = react.useGesture({ onDrag: function onDrag(_ref) { var pinching = _ref.pinching, cancel = _ref.cancel, _ref$offset = _ref.offset, x = _ref$offset[0], y = _ref$offset[1]; if (pinching) return cancel(); api.start({ x: x, y: y }); }, onPinch: utils.pinch.zoom([springs, api], { ref: ref }) }, { drag: { from: function from() { return [springs[0].x.get(), springs[0].y.get()]; }, bound: ref == null ? void 0 : ref.getBoundingClientRect() }, pinch: { scaleBounds: { min: 0.8, max: 3 }, rubberband: true } }); return { bind: bind, springs: springs, state: { currentIndex: 0, itemSize: itemSize }, refCallbacks: refCallbacks }; } exports.useInfiniteHorizontalScroll = useInfiniteHorizontalScroll; exports.useInfiniteVerticalScroll = useInfiniteVerticalScroll; exports.useLimitedHorizontalSwipe = useLimitedHorizontalSwipe; exports.useLimitedVerticalSwipe = useLimitedVerticalSwipe; exports.usePinchZoomAndDrag = usePinchZoomAndDrag; exports.useScrollZoneRef = useScrollZoneRefs; exports.useScrollingAnimationSetup = useInfiniteScrollSetup; //# sourceMappingURL=carousel-hooks.cjs.development.js.map