seamless-scroll-react
Version:
react无缝滚动和单步滚动
414 lines (342 loc) • 12.4 kB
JavaScript
;
Object.defineProperty(exports, '__esModule', { value: true });
function _interopDefault (ex) { return (ex && (typeof ex === 'object') && 'default' in ex) ? ex['default'] : ex; }
var React = require('react');
var React__default = _interopDefault(React);
var throttleDebounce = require('throttle-debounce');
function dataWarm(list) {
if (list && typeof list !== 'boolean' && list.length >= 400) {
console.warn("\u6570\u636E\u8FBE\u5230\u4E86" + list.length + "\u6761\u6709\u70B9\u591A\u54E6~,\u53EF\u80FD\u4F1A\u9020\u6210\u90E8\u5206\u8001\u65E7\u6D4F\u89C8\u5668\u5361\u987F\u3002");
}
}
/**
* 无缝滚动组件
* @param props SeamlessScrollType
* @param ref ref
* @returns ReactNode
*/
var ReactSeamlessScroll = function ReactSeamlessScroll(props, ref) {
var _props$copyNum;
var _props$list = props.list,
list = _props$list === void 0 ? [] : _props$list,
_props$limitScrollNum = props.limitScrollNum,
limitScrollNum = _props$limitScrollNum === void 0 ? 3 : _props$limitScrollNum,
_props$hover = props.hover,
hover = _props$hover === void 0 ? false : _props$hover,
_props$wheel = props.wheel,
wheel = _props$wheel === void 0 ? false : _props$wheel,
_props$isRemUnit = props.isRemUnit,
isRemUnit = _props$isRemUnit === void 0 ? false : _props$isRemUnit,
_props$isAutoScroll = props.isAutoScroll,
isAutoScroll = _props$isAutoScroll === void 0 ? true : _props$isAutoScroll,
_props$ease = props.ease,
ease = _props$ease === void 0 ? 'ease-in' : _props$ease,
_props$delay = props.delay,
delay = _props$delay === void 0 ? 0 : _props$delay,
_props$singleWaitTime = props.singleWaitTime,
singleWaitTime = _props$singleWaitTime === void 0 ? 1000 : _props$singleWaitTime,
_props$direction = props.direction,
direction = _props$direction === void 0 ? 'up' : _props$direction,
_props$singleWidth = props.singleWidth,
singleWidth = _props$singleWidth === void 0 ? 0 : _props$singleWidth,
_props$singleHeight = props.singleHeight,
singleHeight = _props$singleHeight === void 0 ? 0 : _props$singleHeight,
_props$step = props.step,
step = _props$step === void 0 ? 1 : _props$step,
_props$count = props.count,
count = _props$count === void 0 ? -1 : _props$count,
children = props.children,
wrapperClassName = props.wrapperClassName,
wrapperHeight = props.wrapperHeight;
var copyNum = new Array((_props$copyNum = props.copyNum) != null ? _props$copyNum : 1).fill(null);
var scrollRef = React.useRef(null);
var slotListRef = React.useRef(null);
var realBoxRef = React.useRef(null);
var realBoxWidth = React.useRef(0);
var realBoxHeight = React.useRef(0);
var reqFrame = React.useRef(null);
var singleWaitTimeout = React.useRef(null);
var _useState = React.useState(0),
xPosState = _useState[0],
setXpos = _useState[1];
var _useState2 = React.useState(0),
yPosState = _useState2[0],
setYpos = _useState2[1];
var xPos = React.useRef(xPosState);
var yPos = React.useRef(yPosState);
xPos.current = xPosState;
yPos.current = yPosState; // 记录滚动次数
var _count = React.useRef(0); // 是否能滚动
var isScroll = React.useMemo(function () {
return list ? list.length >= limitScrollNum : false;
}, [limitScrollNum, list]); // 真实盒子的样式
var realBoxStyle = React.useMemo(function () {
return {
width: realBoxWidth.current ? realBoxWidth.current + "px" : 'auto',
transform: "translate(" + xPosState + "px," + yPosState + "px)",
transition: "all " + (typeof ease === 'string' ? ease : 'cubic-bezier(' + ease.x1 + ',' + ease.y1 + ',' + ease.x2 + ',' + ease.y2 + ')') + " " + delay + "ms",
overflow: 'hidden'
};
}, [delay, ease, xPosState, yPosState]); // 是否水平滚动
var isHorizontal = React.useMemo(function () {
return direction === 'left' || direction === 'right';
}, [direction]); // 是否开启鼠标移入事件
var isHoverStop = React.useMemo(function () {
return hover && isAutoScroll && isScroll;
}, [hover, isScroll, isAutoScroll]); // children列表div样式
var floatStyle = React.useMemo(function () {
return isHorizontal ? {
float: 'left',
overflow: 'hidden'
} : {
overflow: 'hidden'
};
}, [isHorizontal]); // 基础字体大小 默认为1px
var baseFontSize = React.useMemo(function () {
return isRemUnit ? parseInt(globalThis.window.getComputedStyle(globalThis.document.documentElement, null).fontSize) : 1;
}, [isRemUnit]); // 横向单步大小
var realSingleStopWidth = React.useMemo(function () {
return singleWidth * baseFontSize;
}, [baseFontSize, singleWidth]); // 纵向单步大小
var realSingleStopHeight = React.useMemo(function () {
return singleHeight * baseFontSize;
}, [singleHeight, baseFontSize]); // 滚动频率
var stepCount = React.useMemo(function () {
var singleStep;
var _step = step;
if (isHorizontal) {
singleStep = realSingleStopWidth;
} else {
singleStep = realSingleStopHeight;
}
if (singleStep > 0 && singleStep % _step > 0) {
console.warn('如果设置了单步滚动,step 需是单步大小的约数,否则无法保证单步滚动结束的位置是否准确。~~~~~');
}
return _step;
}, [isHorizontal, step, realSingleStopHeight, realSingleStopWidth]); // 滚动动画
var animation = function animation(_direction, _step, isWheel) {
reqFrame.current = requestAnimationFrame(function () {
// 无缝滚动 因为复制了一份数组所以要除以2
var h = realBoxHeight.current / 2;
var w = realBoxWidth.current / 2;
if (_direction === 'up') {
if (Math.abs(yPos.current) >= h) {
setYpos(0);
_count.current += 1;
} // 这儿不能else判断 因为单位滚动到临界值时会导致滚动不很连续
setYpos(function (item) {
return item -= _step;
});
} else if (_direction === 'down') {
if (yPos.current >= 0) {
setYpos(h * -1);
_count.current += 1;
}
setYpos(function (item) {
return item += _step;
});
} else if (_direction === 'left') {
if (Math.abs(xPos.current) >= w) {
setXpos(0);
_count.current += 1;
}
setXpos(function (item) {
return item -= _step;
});
} else if (_direction === 'right') {
if (xPos.current >= 0) {
setXpos(w * -1);
_count.current += 1;
}
setXpos(function (item) {
return item += _step;
});
} // 当滚轮滑动时不能单步滚动
if (isWheel) return;
if (singleWaitTimeout.current) {
clearTimeout(singleWaitTimeout.current);
} // 单步滚动
if (!!realSingleStopHeight) {
if (Math.abs(yPos.current) % realSingleStopHeight === 0) {
singleWaitTimeout.current = setTimeout(function () {
move();
}, singleWaitTime);
} else {
move();
}
} else if (!!realSingleStopWidth) {
if (Math.abs(xPos.current) % realSingleStopWidth < _step) {
singleWaitTimeout.current = setTimeout(function () {
move();
}, singleWaitTime);
} else {
move();
}
} else {
move();
}
});
}; // 滚动动画核心
var move = function move() {
cancle();
if (!isAutoScroll || !isScroll || _count.current === count) {
_count.current = 0;
return;
}
animation(direction, stepCount, false);
}; // 初始化滚动
var initMove = function initMove() {
dataWarm(list); // 是否横线滚动
if (isHorizontal) {
var slotListWidth = slotListRef.current.offsetWidth;
realBoxWidth.current = slotListWidth * 2 + 1;
} else {
realBoxHeight.current = realBoxRef.current.offsetHeight;
}
move();
}; // 滚轮事件
var throttleFunc = throttleDebounce.throttle(30, function (e) {
cancle();
var singleStep = !!realSingleStopHeight ? realSingleStopHeight : 20;
if (e.deltaY < 0) {
animation('down', singleStep, true);
}
if (e.deltaY > 0) {
animation('up', singleStep, true);
}
}); // 滚轮事件
var _onWheel = function onWheel(e) {
throttleFunc(e);
}; // 开始滚动
var startMove = function startMove() {
move();
}; // 停止滚动
var stopMove = function stopMove() {
if (singleWaitTimeout.current) {
clearTimeout(singleWaitTimeout.current);
}
cancle();
}; // 取消滚动id 避免闭包问题
var cancle = function cancle() {
cancelAnimationFrame(reqFrame.current);
reqFrame.current = null;
};
var reset = function reset() {
if (singleWaitTimeout.current) {
clearTimeout(singleWaitTimeout.current);
}
cancle();
initMove();
};
React.useEffect(function () {
if (isAutoScroll) {
startMove();
} else {
stopMove();
}
}, [isAutoScroll]);
React.useEffect(function () {
if (count !== 0) {
startMove();
}
}, [count]); // 初始化
React.useEffect(function () {
cancle();
if (singleWaitTimeout.current) {
clearTimeout(singleWaitTimeout.current);
} // 如果数据list长度能滚动并且配置了自动滚动
if (isScroll && isAutoScroll) {
initMove();
}
return function () {
stopMove();
};
}, [list]); // 提供的方法
React.useImperativeHandle(ref, function () {
return {
onReset: function onReset() {
reset();
},
onStopMove: function onStopMove() {
stopMove();
},
onStartMove: function onStartMove() {
startMove();
}
};
}); // children列表div
var getHtmlMemo = React.useMemo(function () {
return React__default.createElement(React__default.Fragment, null, React__default.createElement("div", {
ref: slotListRef,
style: floatStyle
}, children), isScroll ? copyNum.map(function (_, i) {
return React__default.createElement("div", {
key: i,
style: floatStyle
}, children);
}) : null);
}, [list]);
return React__default.createElement("div", {
ref: scrollRef,
className: wrapperClassName,
style: {
height: wrapperHeight || realBoxHeight.current / 2,
overflow: 'hidden'
}
}, wheel && hover ? React__default.createElement("div", {
ref: realBoxRef,
style: realBoxStyle,
onMouseEnter: function onMouseEnter() {
if (isHoverStop) {
stopMove();
}
},
onMouseLeave: function onMouseLeave() {
if (isHoverStop) {
startMove();
}
},
onWheel: function onWheel(e) {
if (isHoverStop) {
_onWheel(e);
}
}
}, getHtmlMemo) : React__default.createElement("div", {
ref: realBoxRef,
style: realBoxStyle,
onMouseEnter: function onMouseEnter() {
if (isHoverStop) {
stopMove();
}
},
onMouseLeave: function onMouseLeave() {
if (isHoverStop) {
startMove();
}
}
}, getHtmlMemo));
};
var SeamlessScroll = /*#__PURE__*/React.memo( /*#__PURE__*/React.forwardRef(ReactSeamlessScroll));
/**
* 处理requestAnimationFrame兼容问题
*/
globalThis.window.cancelAnimationFrame = /*#__PURE__*/function () {
return globalThis.window.cancelAnimationFrame || // @ts-ignore
globalThis.window.webkitCancelAnimationFrame || // @ts-ignore
globalThis.window.mozCancelAnimationFrame || // @ts-ignore
globalThis.window.oCancelAnimationFrame || // @ts-ignore
globalThis.window.msCancelAnimationFrame || function (id) {
return globalThis.window.clearTimeout(id);
};
}();
globalThis.window.requestAnimationFrame = /*#__PURE__*/function () {
return globalThis.window.requestAnimationFrame || // @ts-ignore
globalThis.window.webkitRequestAnimationFrame || // @ts-ignore
globalThis.window.mozRequestAnimationFrame || // @ts-ignore
globalThis.window.oRequestAnimationFrame || // @ts-ignore
globalThis.window.msRequestAnimationFrame || function (callback) {
return globalThis.window.setTimeout(callback, 1000 / 60);
};
}();
exports.default = SeamlessScroll;
//# sourceMappingURL=seamless-scroll-react.cjs.development.js.map