antd-mobile
Version:
<div align="center">
352 lines (351 loc) • 13.1 kB
JavaScript
Object.defineProperty(exports, "__esModule", {
value: true
});
exports.Swiper = void 0;
var _react = _interopRequireWildcard(require("react"));
var _nativeProps = require("../../utils/native-props");
var _withDefaultProps = require("../../utils/with-default-props");
var _classnames = _interopRequireDefault(require("classnames"));
var _swiperItem = require("./swiper-item");
var _devLog = require("../../utils/dev-log");
var _web = require("@react-spring/web");
var _react2 = require("@use-gesture/react");
var _pageIndicator = _interopRequireDefault(require("../page-indicator"));
var _stagedComponents = require("staged-components");
var _useRefState = require("../../utils/use-ref-state");
var _bound = require("../../utils/bound");
var _ahooks = require("ahooks");
var _withFuncProps = require("../../utils/with-func-props");
function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
function _getRequireWildcardCache(nodeInterop) { if (typeof WeakMap !== "function") return null; var cacheBabelInterop = new WeakMap(); var cacheNodeInterop = new WeakMap(); return (_getRequireWildcardCache = function (nodeInterop) { return nodeInterop ? cacheNodeInterop : cacheBabelInterop; })(nodeInterop); }
function _interopRequireWildcard(obj, nodeInterop) { if (!nodeInterop && obj && obj.__esModule) { return obj; } if (obj === null || typeof obj !== "object" && typeof obj !== "function") { return { default: obj }; } var cache = _getRequireWildcardCache(nodeInterop); if (cache && cache.has(obj)) { return cache.get(obj); } var newObj = {}; var hasPropertyDescriptor = Object.defineProperty && Object.getOwnPropertyDescriptor; for (var key in obj) { if (key !== "default" && Object.prototype.hasOwnProperty.call(obj, key)) { var desc = hasPropertyDescriptor ? Object.getOwnPropertyDescriptor(obj, key) : null; if (desc && (desc.get || desc.set)) { Object.defineProperty(newObj, key, desc); } else { newObj[key] = obj[key]; } } } newObj.default = obj; if (cache) { cache.set(obj, newObj); } return newObj; }
const classPrefix = `adm-swiper`;
const eventToPropRecord = {
'mousedown': 'onMouseDown',
'mousemove': 'onMouseMove',
'mouseup': 'onMouseUp'
};
const defaultProps = {
defaultIndex: 0,
allowTouchMove: true,
autoplay: false,
autoplayInterval: 3000,
loop: false,
direction: 'horizontal',
slideSize: 100,
trackOffset: 0,
stuckAtBoundary: true,
rubberband: true,
stopPropagation: []
};
let currentUid;
const Swiper = (0, _react.forwardRef)((0, _stagedComponents.staged)((p, ref) => {
const props = (0, _withDefaultProps.mergeProps)(defaultProps, p);
const {
direction,
total,
children,
indicator
} = props;
const [uid] = (0, _react.useState)({});
const timeoutRef = (0, _react.useRef)(null);
const isVertical = direction === 'vertical';
const slideRatio = props.slideSize / 100;
const offsetRatio = props.trackOffset / 100;
const {
validChildren,
count,
renderChildren
} = (0, _react.useMemo)(() => {
let count = 0;
let renderChildren = undefined;
let validChildren = undefined;
if (typeof children === 'function') {
renderChildren = children;
} else {
validChildren = _react.default.Children.map(children, child => {
if (!_react.default.isValidElement(child)) return null;
if (child.type !== _swiperItem.SwiperItem) {
(0, _devLog.devWarning)('Swiper', 'The children of `Swiper` must be `Swiper.Item` components.');
return null;
}
count++;
return child;
});
}
return {
renderChildren,
validChildren,
count
};
}, [children]);
const mergedTotal = total !== null && total !== void 0 ? total : count;
if (mergedTotal === 0 || !validChildren && !renderChildren) {
(0, _devLog.devWarning)('Swiper', '`Swiper` needs at least one child.');
return null;
}
return () => {
let loop = props.loop;
if (slideRatio * (mergedTotal - 1) < 1) {
loop = false;
}
const trackRef = (0, _react.useRef)(null);
function getSlidePixels() {
const track = trackRef.current;
if (!track) return 0;
const trackPixels = isVertical ? track.offsetHeight : track.offsetWidth;
return trackPixels * props.slideSize / 100;
}
const [current, setCurrent, getCurrent] = (0, _ahooks.useGetState)(props.defaultIndex);
const [dragging, setDragging, draggingRef] = (0, _useRefState.useRefState)(false);
function boundIndex(current) {
let min = 0;
let max = mergedTotal - 1;
if (props.stuckAtBoundary) {
min += offsetRatio / slideRatio;
max -= (1 - slideRatio - offsetRatio) / slideRatio;
}
return (0, _bound.bound)(current, min, max);
}
const [{
position
}, api] = (0, _web.useSpring)(() => ({
position: boundIndex(current) * 100,
config: {
tension: 200,
friction: 30
},
onRest: () => {
if (draggingRef.current) return;
if (!loop) return;
const rawX = position.get();
const totalWidth = 100 * mergedTotal;
const standardPosition = modulus(rawX, totalWidth);
if (standardPosition === rawX) return;
api.start({
position: standardPosition,
immediate: true
});
}
}), [mergedTotal]);
const dragCancelRef = (0, _react.useRef)(null);
function forceCancelDrag() {
var _a;
(_a = dragCancelRef.current) === null || _a === void 0 ? void 0 : _a.call(dragCancelRef);
draggingRef.current = false;
}
const bind = (0, _react2.useDrag)(state => {
dragCancelRef.current = state.cancel;
if (!state.intentional) return;
if (state.first && !currentUid) {
currentUid = uid;
}
if (currentUid !== uid) return;
currentUid = state.last ? undefined : uid;
const slidePixels = getSlidePixels();
if (!slidePixels) return;
const paramIndex = isVertical ? 1 : 0;
const offset = state.offset[paramIndex];
const direction = state.direction[paramIndex];
const velocity = state.velocity[paramIndex];
setDragging(true);
if (!state.last) {
api.start({
position: offset * 100 / slidePixels,
immediate: true
});
} else {
const minIndex = Math.floor(offset / slidePixels);
const maxIndex = minIndex + 1;
const index = Math.round((offset + velocity * 2000 * direction) / slidePixels);
swipeTo((0, _bound.bound)(index, minIndex, maxIndex));
window.setTimeout(() => {
setDragging(false);
});
}
}, {
transform: ([x, y]) => [-x, -y],
from: () => {
const slidePixels = getSlidePixels();
return [position.get() / 100 * slidePixels, position.get() / 100 * slidePixels];
},
triggerAllEvents: true,
bounds: () => {
if (loop) return {};
const slidePixels = getSlidePixels();
const lowerBound = boundIndex(0) * slidePixels;
const upperBound = boundIndex(mergedTotal - 1) * slidePixels;
return isVertical ? {
top: lowerBound,
bottom: upperBound
} : {
left: lowerBound,
right: upperBound
};
},
rubberband: props.rubberband,
axis: isVertical ? 'y' : 'x',
preventScroll: !isVertical,
pointer: {
touch: true
}
});
function swipeTo(index, immediate = false) {
var _a;
const roundedIndex = Math.round(index);
const targetIndex = loop ? modulus(roundedIndex, mergedTotal) : (0, _bound.bound)(roundedIndex, 0, mergedTotal - 1);
if (targetIndex !== getCurrent()) {
(_a = props.onIndexChange) === null || _a === void 0 ? void 0 : _a.call(props, targetIndex);
}
setCurrent(targetIndex);
api.start({
position: (loop ? roundedIndex : boundIndex(roundedIndex)) * 100,
immediate
});
}
function swipeNext() {
swipeTo(Math.round(position.get() / 100) + 1);
}
function swipePrev() {
swipeTo(Math.round(position.get() / 100) - 1);
}
(0, _react.useImperativeHandle)(ref, () => ({
swipeTo,
swipeNext,
swipePrev
}));
(0, _ahooks.useIsomorphicLayoutEffect)(() => {
const maxIndex = mergedTotal - 1;
if (current > maxIndex) {
swipeTo(maxIndex, true);
}
});
const {
autoplay,
autoplayInterval
} = props;
const runTimeSwiper = () => {
timeoutRef.current = window.setTimeout(() => {
if (autoplay === 'reverse') {
swipePrev();
} else {
swipeNext();
}
runTimeSwiper();
}, autoplayInterval);
};
(0, _react.useEffect)(() => {
if (!autoplay || dragging) return;
runTimeSwiper();
return () => {
if (timeoutRef.current) window.clearTimeout(timeoutRef.current);
};
}, [autoplay, autoplayInterval, dragging, mergedTotal]);
// ============================== Render ==============================
// Render Item
function renderItem(index, child) {
let itemStyle = {};
if (loop) {
itemStyle = {
[isVertical ? 'y' : 'x']: position.to(position => {
let finalPosition = -position + index * 100;
const totalWidth = mergedTotal * 100;
const flagWidth = totalWidth / 2;
finalPosition = modulus(finalPosition + flagWidth, totalWidth) - flagWidth;
return `${finalPosition}%`;
}),
[isVertical ? 'top' : 'left']: `-${index * 100}%`
};
}
return _react.default.createElement(_web.animated.div, {
className: (0, _classnames.default)(`${classPrefix}-slide`, {
[`${classPrefix}-slide-active`]: current === index
}),
style: itemStyle,
key: index
}, child);
}
function renderItems() {
if (renderChildren && total) {
const offsetCount = 2;
const startIndex = Math.max(current - offsetCount, 0);
const endIndex = Math.min(current + offsetCount, total - 1);
const items = [];
for (let index = startIndex; index <= endIndex; index += 1) {
items.push(renderItem(index, renderChildren(index)));
}
return _react.default.createElement(_react.default.Fragment, null, _react.default.createElement("div", {
className: `${classPrefix}-slide-placeholder`,
style: {
width: `${startIndex * 100}%`
}
}), items);
}
return _react.default.Children.map(validChildren, (child, index) => {
return renderItem(index, child);
});
}
// Render Track Inner
function renderTrackInner() {
if (loop) {
return _react.default.createElement("div", {
className: `${classPrefix}-track-inner`
}, renderItems());
} else {
return _react.default.createElement(_web.animated.div, {
className: `${classPrefix}-track-inner`,
style: {
[isVertical ? 'y' : 'x']: position.to(position => `${-position}%`)
}
}, renderItems());
}
}
// Render
const style = {
'--slide-size': `${props.slideSize}%`,
'--track-offset': `${props.trackOffset}%`
};
const dragProps = Object.assign({}, props.allowTouchMove ? bind() : {});
const stopPropagationProps = {};
for (const key of props.stopPropagation) {
const prop = eventToPropRecord[key];
stopPropagationProps[prop] = function (e) {
e.stopPropagation();
};
}
const mergedProps = (0, _withFuncProps.mergeFuncProps)(dragProps, stopPropagationProps);
let indicatorNode = null;
if (typeof indicator === 'function') {
indicatorNode = indicator(mergedTotal, current);
} else if (indicator !== false) {
indicatorNode = _react.default.createElement("div", {
className: `${classPrefix}-indicator`
}, _react.default.createElement(_pageIndicator.default, Object.assign({}, props.indicatorProps, {
total: mergedTotal,
current: current,
direction: direction
})));
}
return (0, _nativeProps.withNativeProps)(props, _react.default.createElement("div", {
className: (0, _classnames.default)(classPrefix, `${classPrefix}-${direction}`),
style: style
}, _react.default.createElement("div", Object.assign({
ref: trackRef,
className: (0, _classnames.default)(`${classPrefix}-track`, {
[`${classPrefix}-track-allow-touch-move`]: props.allowTouchMove
}),
onClickCapture: e => {
if (draggingRef.current) {
e.stopPropagation();
}
forceCancelDrag();
}
}, mergedProps), renderTrackInner()), indicatorNode));
};
}));
exports.Swiper = Swiper;
function modulus(value, division) {
const remainder = value % division;
return remainder < 0 ? remainder + division : remainder;
}
;