UNPKG

@orca-fe/hooks

Version:

React Hooks Collections

202 lines (191 loc) 8.56 kB
import _objectSpread from "@babel/runtime/helpers/esm/objectSpread2"; import _slicedToArray from "@babel/runtime/helpers/esm/slicedToArray"; import { useBoolean, useLatest, useMemoizedFn, useRafState, useSize, useThrottleFn } from 'ahooks'; import { useEffect, useRef } from 'react'; import { round } from 'lodash-es'; import { getTargetElement } from "./utils/domTarget"; import useAnimationFrame from "./useAnimationFrame"; import useEffectWithTarget from "./useEffectWithTarget"; export var MANUAL_SCROLL_UP = 'up'; // 向上滚动 export var MANUAL_SCROLL_DOWN = 'down'; // 向下滚动 export var MANUAL_SCROLL_LEFT = 'left'; // 向左滚动 export var MANUAL_SCROLL_RIGHT = 'right'; // 向右滚动 // 基准帧间隔 var baseFrameInterval = 16.67; // 是否水平滚动 var isHorizScroll = direction => direction === MANUAL_SCROLL_LEFT || direction === MANUAL_SCROLL_RIGHT; export default function useManualScroll(target, options = {}) { var _options$defaultScrol = options.defaultScrollStep, defaultScrollStep = _options$defaultScrol === void 0 ? 200 : _options$defaultScrol, _options$duration = options.duration, duration = _options$duration === void 0 ? 300 : _options$duration, _options$shouldUpdate = options.shouldUpdate, shouldUpdate = _options$shouldUpdate === void 0 ? () => true : _options$shouldUpdate; var _this = useRef({ scrollDistance: 0, lastFrameTime: null, animDuration: 0, direction: null, scrollStep: defaultScrollStep, scrollOffset: { x: 0, y: 0 } }).current; var _useBoolean = useBoolean(false), _useBoolean2 = _slicedToArray(_useBoolean, 2), scrolling = _useBoolean2[0], _useBoolean2$ = _useBoolean2[1], startScrolling = _useBoolean2$.setTrue, stopScrolling = _useBoolean2$.setFalse; var dom = getTargetElement(target); var size = useSize(target); // 透传出的容器滚动位置信息(依赖 shouldUpdate) var _useRafState = useRafState(), _useRafState2 = _slicedToArray(_useRafState, 2), position = _useRafState2[0], setPosition = _useRafState2[1]; // 保存容器最新的滚动位置信息 var latestPositionRef = useRef(); var shouldUpdateRef = useLatest(shouldUpdate); // 根据当前位置获取各方位滚动状态 var scrollBoundaryState = useMemoizedFn(currentPosition => { var _currentPosition$left, _size$width, _dom$scrollWidth, _currentPosition$top, _size$height, _dom$scrollHeight; var scrollToLeft = (currentPosition === null || currentPosition === void 0 ? void 0 : currentPosition.left) === 0; var scrollToRight = Math.ceil(((_currentPosition$left = currentPosition === null || currentPosition === void 0 ? void 0 : currentPosition.left) !== null && _currentPosition$left !== void 0 ? _currentPosition$left : 0) + ((_size$width = size === null || size === void 0 ? void 0 : size.width) !== null && _size$width !== void 0 ? _size$width : 0)) >= ((_dom$scrollWidth = dom === null || dom === void 0 ? void 0 : dom.scrollWidth) !== null && _dom$scrollWidth !== void 0 ? _dom$scrollWidth : 0); var scrollToTop = (currentPosition === null || currentPosition === void 0 ? void 0 : currentPosition.top) === 0; var scrollToBottom = Math.ceil(((_currentPosition$top = currentPosition === null || currentPosition === void 0 ? void 0 : currentPosition.top) !== null && _currentPosition$top !== void 0 ? _currentPosition$top : 0) + ((_size$height = size === null || size === void 0 ? void 0 : size.height) !== null && _size$height !== void 0 ? _size$height : 0)) >= ((_dom$scrollHeight = dom === null || dom === void 0 ? void 0 : dom.scrollHeight) !== null && _dom$scrollHeight !== void 0 ? _dom$scrollHeight : 0); return { scrollToLeft, scrollToRight, scrollToTop, scrollToBottom }; }); // 获取当前帧间隔 var currentFrameInterval = useMemoizedFn((ms, frameTime) => { if (!_this.lastFrameTime) { _this.lastFrameTime = frameTime; } var frameInterval = ms - _this.lastFrameTime || baseFrameInterval; return _this.animDuration + frameInterval > duration ? duration - _this.animDuration : frameInterval; }); // 获取当前帧滚动量 var currentFrameStep = useMemoizedFn(frameInterval => { // 基准帧滚动量 var baseFrameStep = _this.scrollStep / (duration / baseFrameInterval); // 帧间隔偏差比例 var deviationRatio = frameInterval / baseFrameInterval; // 当前帧滚动量 var frameStep = deviationRatio * baseFrameStep; return _this.scrollDistance + frameStep > _this.scrollStep ? _this.scrollStep - _this.scrollDistance : frameStep; }); // 是否抵达边界 var isReachToBoundary = useMemoizedFn(() => { var latestPosition = scrollBoundaryState(latestPositionRef.current); if (_this.direction === MANUAL_SCROLL_LEFT) return latestPosition.scrollToLeft; if (_this.direction === MANUAL_SCROLL_RIGHT) return latestPosition.scrollToRight; if (_this.direction === MANUAL_SCROLL_UP) return latestPosition.scrollToTop; if (_this.direction === MANUAL_SCROLL_DOWN) return latestPosition.scrollToBottom; return false; }); // modify from `useScroll` useEffectWithTarget(() => { var el = getTargetElement(target); if (!el) { return; } var updatePosition = () => { var _shouldUpdateRef$curr; var newPosition = { left: el.scrollLeft, top: el.scrollTop }; if ((_shouldUpdateRef$curr = shouldUpdateRef.current) !== null && _shouldUpdateRef$curr !== void 0 && _shouldUpdateRef$curr.call(shouldUpdateRef, newPosition)) { setPosition(newPosition); } latestPositionRef.current = newPosition; }; updatePosition(); el.addEventListener('scroll', updatePosition); // eslint-disable-next-line consistent-return return () => { el.removeEventListener('scroll', updatePosition); }; }, [], target); var rafHandler = useAnimationFrame((ms, frameTime) => { if (!dom || !_this.direction || _this.animDuration >= duration) { stopScrolling(); return; } if (isReachToBoundary()) { if (isHorizScroll(_this.direction)) _this.scrollOffset.x = 0;else _this.scrollOffset.y = 0; stopScrolling(); return; } // 当前帧间隔 var frameInterval = currentFrameInterval(ms, frameTime); // 当前帧滚动量 var frameStep = currentFrameStep(frameInterval); // 累计滚动量 _this.scrollDistance += frameStep; // 累计动画时间 _this.animDuration += frameInterval; var xScrollValue = 0; var yScrollValue = 0; // 向左滚动 if (_this.direction === MANUAL_SCROLL_LEFT) { xScrollValue = round(dom.scrollLeft - frameStep, 2) + _this.scrollOffset.x; } // 向右滚动 if (_this.direction === MANUAL_SCROLL_RIGHT) { xScrollValue = round(dom.scrollLeft + frameStep, 2) + _this.scrollOffset.x; } // 向上滚动 if (_this.direction === MANUAL_SCROLL_UP) { yScrollValue = round(dom.scrollTop - frameStep, 2) + _this.scrollOffset.y; } // 向下滚动 if (_this.direction === MANUAL_SCROLL_DOWN) { yScrollValue = round(dom.scrollTop + frameStep, 2) + _this.scrollOffset.y; } // 滚动并更新误差值 if (isHorizScroll(_this.direction)) { dom.scrollLeft = xScrollValue; _this.scrollOffset.x = xScrollValue - dom.scrollLeft; } else { dom.scrollTop = yScrollValue; _this.scrollOffset.y = yScrollValue - dom.scrollTop; } }, { manual: true }); var run = (direction, scrollStep = defaultScrollStep) => { _this.direction = direction; _this.scrollStep = scrollStep; var latestPosition = scrollBoundaryState(latestPositionRef.current); if (direction === MANUAL_SCROLL_LEFT && latestPosition.scrollToLeft) return; if (direction === MANUAL_SCROLL_RIGHT && latestPosition.scrollToRight) return; if (direction === MANUAL_SCROLL_UP && latestPosition.scrollToTop) return; if (direction === MANUAL_SCROLL_DOWN && latestPosition.scrollToBottom) return; startScrolling(); }; var _useThrottleFn = useThrottleFn(run, { wait: duration }), throttleRun = _useThrottleFn.run; useEffect(() => { if (scrolling) { _this.animDuration = 0; _this.scrollDistance = 0; _this.lastFrameTime = null; rafHandler.start(); } else { rafHandler.stop(); } }, [scrolling]); return _objectSpread({ position, run: useMemoizedFn(throttleRun) }, scrollBoundaryState(position)); }