@orca-fe/hooks
Version:
React Hooks Collections
216 lines (204 loc) • 9.68 kB
JavaScript
"use strict";
var _interopRequireDefault = require("@babel/runtime/helpers/interopRequireDefault").default;
Object.defineProperty(exports, "__esModule", {
value: true
});
exports.MANUAL_SCROLL_UP = exports.MANUAL_SCROLL_RIGHT = exports.MANUAL_SCROLL_LEFT = exports.MANUAL_SCROLL_DOWN = void 0;
exports.default = useManualScroll;
var _objectSpread2 = _interopRequireDefault(require("@babel/runtime/helpers/objectSpread2"));
var _slicedToArray2 = _interopRequireDefault(require("@babel/runtime/helpers/slicedToArray"));
var _ahooks = require("ahooks");
var _react = require("react");
var _lodashEs = require("lodash-es");
var _domTarget = require("./utils/domTarget");
var _useAnimationFrame = _interopRequireDefault(require("./useAnimationFrame"));
var _useEffectWithTarget = _interopRequireDefault(require("./useEffectWithTarget"));
var MANUAL_SCROLL_UP = exports.MANUAL_SCROLL_UP = 'up'; // 向上滚动
var MANUAL_SCROLL_DOWN = exports.MANUAL_SCROLL_DOWN = 'down'; // 向下滚动
var MANUAL_SCROLL_LEFT = exports.MANUAL_SCROLL_LEFT = 'left'; // 向左滚动
var MANUAL_SCROLL_RIGHT = exports.MANUAL_SCROLL_RIGHT = 'right'; // 向右滚动
// 基准帧间隔
var baseFrameInterval = 16.67;
// 是否水平滚动
var isHorizScroll = function isHorizScroll(direction) {
return direction === MANUAL_SCROLL_LEFT || direction === MANUAL_SCROLL_RIGHT;
};
function useManualScroll(target) {
var options = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : {};
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 ? function () {
return true;
} : _options$shouldUpdate;
var _this = (0, _react.useRef)({
scrollDistance: 0,
lastFrameTime: null,
animDuration: 0,
direction: null,
scrollStep: defaultScrollStep,
scrollOffset: {
x: 0,
y: 0
}
}).current;
var _useBoolean = (0, _ahooks.useBoolean)(false),
_useBoolean2 = (0, _slicedToArray2.default)(_useBoolean, 2),
scrolling = _useBoolean2[0],
_useBoolean2$ = _useBoolean2[1],
startScrolling = _useBoolean2$.setTrue,
stopScrolling = _useBoolean2$.setFalse;
var dom = (0, _domTarget.getTargetElement)(target);
var size = (0, _ahooks.useSize)(target);
// 透传出的容器滚动位置信息(依赖 shouldUpdate)
var _useRafState = (0, _ahooks.useRafState)(),
_useRafState2 = (0, _slicedToArray2.default)(_useRafState, 2),
position = _useRafState2[0],
setPosition = _useRafState2[1];
// 保存容器最新的滚动位置信息
var latestPositionRef = (0, _react.useRef)();
var shouldUpdateRef = (0, _ahooks.useLatest)(shouldUpdate);
// 根据当前位置获取各方位滚动状态
var scrollBoundaryState = (0, _ahooks.useMemoizedFn)(function (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: scrollToLeft,
scrollToRight: scrollToRight,
scrollToTop: scrollToTop,
scrollToBottom: scrollToBottom
};
});
// 获取当前帧间隔
var currentFrameInterval = (0, _ahooks.useMemoizedFn)(function (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 = (0, _ahooks.useMemoizedFn)(function (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 = (0, _ahooks.useMemoizedFn)(function () {
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`
(0, _useEffectWithTarget.default)(function () {
var el = (0, _domTarget.getTargetElement)(target);
if (!el) {
return;
}
var updatePosition = function 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 function () {
el.removeEventListener('scroll', updatePosition);
};
}, [], target);
var rafHandler = (0, _useAnimationFrame.default)(function (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 = (0, _lodashEs.round)(dom.scrollLeft - frameStep, 2) + _this.scrollOffset.x;
}
// 向右滚动
if (_this.direction === MANUAL_SCROLL_RIGHT) {
xScrollValue = (0, _lodashEs.round)(dom.scrollLeft + frameStep, 2) + _this.scrollOffset.x;
}
// 向上滚动
if (_this.direction === MANUAL_SCROLL_UP) {
yScrollValue = (0, _lodashEs.round)(dom.scrollTop - frameStep, 2) + _this.scrollOffset.y;
}
// 向下滚动
if (_this.direction === MANUAL_SCROLL_DOWN) {
yScrollValue = (0, _lodashEs.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 = function run(direction) {
var scrollStep = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : 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 = (0, _ahooks.useThrottleFn)(run, {
wait: duration
}),
throttleRun = _useThrottleFn.run;
(0, _react.useEffect)(function () {
if (scrolling) {
_this.animDuration = 0;
_this.scrollDistance = 0;
_this.lastFrameTime = null;
rafHandler.start();
} else {
rafHandler.stop();
}
}, [scrolling]);
return (0, _objectSpread2.default)({
position: position,
run: (0, _ahooks.useMemoizedFn)(throttleRun)
}, scrollBoundaryState(position));
}