UNPKG

@nutui/nutui-react

Version:

京东风格的轻量级移动端 React 组件库,支持一套代码生成 H5 和小程序

260 lines (259 loc) 10.5 kB
import { _ as _define_property } from "@swc/helpers/_/_define_property"; import { _ as _object_spread } from "@swc/helpers/_/_object_spread"; import { _ as _sliced_to_array } from "@swc/helpers/_/_sliced_to_array"; import React, { useEffect, useMemo, useRef, useState } from "react"; import { useDrag } from "@use-gesture/react"; import { useSpring } from "@react-spring/web"; import classNames from "classnames"; import Indicator from "../indicator"; import { getRefValue, useRefState } from "../../hooks/use-ref-state"; import { defaultEffect } from "./effects/default"; import { focusEffect, updateTransform, useList } from "./effects/focus"; var defaultProps = { direction: 'horizontal', indicator: false, loop: false, duration: 3000, autoPlay: false, defaultValue: 0, touchable: true, effect: undefined }; export var Swiper = /*#__PURE__*/ React.forwardRef(function(props, ref) { var boundIndex = function boundIndex(current) { var min = 0; var max = count - 1; if (current === max && !loop && props.slideSize) { var slideSize = props.slideSize; var swiperSize = getSwiperSize(); var ratio = (swiperSize - slideSize) / slideSize; return bound(current, min, max - ratio); } return current; }; var classPrefix = 'nut-swiper'; var _$_object_spread = _object_spread({}, defaultProps, props), children = _$_object_spread.children, direction = _$_object_spread.direction, indicator = _$_object_spread.indicator, loop = _$_object_spread.loop, effect = _$_object_spread.effect, autoPlay = _$_object_spread.autoPlay, touchable = _$_object_spread.touchable, defaultValue = _$_object_spread.defaultValue, duration = _$_object_spread.duration, style = _$_object_spread.style, className = _$_object_spread.className; var isVertical = direction === 'vertical'; var count = useMemo(function() { var c = 0; React.Children.map(children, function(child, index) { c += 1; }); return c; }, [ children ]); var getSlideSize = function() { if (props.slideSize) return props.slideSize; if (stageRef.current) { if (isVertical) return stageRef.current.offsetHeight; return stageRef.current.offsetWidth; } return 0; }; var getSwiperSize = function() { if (swiperRef.current) { if (isVertical) return swiperRef.current.offsetHeight; return swiperRef.current.offsetWidth; } return 0; }; var bound = function(v, min, max) { if (min !== undefined) { v = Math.max(v, min); } if (max !== undefined) { v = Math.min(v, max); } return v; }; var timeoutRef = useRef(null); var _useState = _sliced_to_array(useState(false), 2), dragging = _useState[0], setDragging = _useState[1]; var _useRefState = _sliced_to_array(useRefState(defaultValue), 2), current = _useRefState[0], setCurrent = _useRefState[1]; var stageRef = useRef(null); var swiperRef = useRef(null); var _useSpring = _sliced_to_array(useSpring(function() { return { x: !isVertical ? current.current * 100 * -1 : 0, y: isVertical ? current.current * 100 * -1 : 0, s: 0, reset: function() {}, config: { tension: 200, friction: 30 } }; }), 2), springs = _useSpring[0], api = _useSpring[1]; useEffect(function() { var _obj; api.start((_obj = {}, _define_property(_obj, isVertical ? 'y' : 'x', boundIndex(current.current) * -1 * 100), _define_property(_obj, "immediate", true), _obj)); }, [ swiperRef.current ]); var swiperDirection = useRef(1); var _useList = _sliced_to_array(useList(effect, count, current), 2), transforms = _useList[0], setTransforms = _useList[1]; // 自动播放 var runTimeSwiper = function() { var durationNumber = typeof duration === 'string' ? parseInt(duration) : duration; var d = typeof autoPlay === 'number' ? autoPlay : durationNumber; timeoutRef.current = window.setTimeout(function() { next(); runTimeSwiper(); }, d); }; useEffect(function() { if (!autoPlay || dragging) return; runTimeSwiper(); return function() { if (timeoutRef.current) window.clearTimeout(timeoutRef.current); }; }, [ autoPlay, duration, dragging, count ]); var to = function(index) { var immediate = arguments.length > 1 && arguments[1] !== void 0 ? arguments[1] : false; var _props_onChange; var targetIndex = bound(index, 0, count - 1); if (loop) { var cycleIndex = index % count; targetIndex = cycleIndex < 0 ? cycleIndex + count : cycleIndex; } setCurrent(targetIndex); (_props_onChange = props.onChange) === null || _props_onChange === void 0 ? void 0 : _props_onChange.call(props, targetIndex); if (effect) { updateTransform(transforms, setTransforms, effect, targetIndex); } var _obj; api.start((_obj = {}, // 这里需要统一成百分比 _define_property(_obj, isVertical ? 'y' : 'x', (loop ? -index : boundIndex(targetIndex) * -1) * 100), _define_property(_obj, "s", 0), _define_property(_obj, "immediate", immediate), _obj)); }; var getSpringsAxis = function() { return springs[isVertical ? 'y' : 'x']; }; var next = function() { to(Math.round(-getSpringsAxis().get() / 100) + 1); }; var prev = function() { to(Math.round(-getSpringsAxis().get() / 100) - 1); }; React.useImperativeHandle(ref, function() { return { to: to, next: next, prev: prev }; }); var bind = useDrag(function(state) { var axis = Number(isVertical); var slideSize = getSlideSize(); var offset = state.offset[axis]; setDragging(!!state.dragging); var distance = state.distance[axis]; swiperDirection.current = state.direction[axis]; if (state.last) { // 计算位置 var swipeDirection = state.direction[axis]; var velocity = state.velocity[axis]; var minIndex = Math.floor(offset / slideSize); var maxIndex = minIndex + 1; var index = Math.round((offset + velocity * 2000 * swipeDirection) / slideSize); to(bound(index, minIndex, maxIndex)); } else { var _obj; // 实时移动,换算百分比 api.start((_obj = {}, _define_property(_obj, isVertical ? 'y' : 'x', -(offset / slideSize * 100)), _define_property(_obj, "s", distance / slideSize), _define_property(_obj, "immediate", true), _obj)); } }, { enabled: touchable, transform: function(param) { var _param = _sliced_to_array(param, 2), x = _param[0], y = _param[1]; return [ -x, -y ]; }, from: function() { // 由百分比转换到像素 var slideSize = getSlideSize(); var x = springs.x.get() / 100 * slideSize; var y = springs.y.get() / 100 * slideSize; return [ -x, -y ]; }, bounds: function() { if (loop) return {}; var slideSize = getSlideSize(); if (isVertical) { return { top: 0, bottom: (count - 1) * slideSize }; } return { left: 0, right: (count - 1) * slideSize }; }, rubberband: true, triggerAllEvents: true, preventScroll: isVertical, axis: isVertical ? 'y' : 'x', pointer: { touch: true } }); var renderIndicator = function() { if (/*#__PURE__*/ React.isValidElement(indicator)) return indicator; if (!indicator) return null; var _obj; return /*#__PURE__*/ React.createElement("div", { className: classNames((_obj = {}, _define_property(_obj, "".concat(classPrefix, "-indicator"), true), _define_property(_obj, "".concat(classPrefix, "-indicator-vertical"), isVertical), _define_property(_obj, "".concat(classPrefix, "-indicator-horizontal"), !isVertical), _obj)) }, /*#__PURE__*/ React.createElement(Indicator, { current: getRefValue(current), total: count, direction: direction })); }; var renderEffect = function() { if (!effect) return defaultEffect({ children: children, getSpringsAxis: getSpringsAxis, loop: loop, count: count, isVertical: isVertical }); if (effect && effect.name === 'focus') { return focusEffect({ children: children, springs: springs, loop: loop, count: count, isVertical: isVertical, effect: effect, current: current, swiperDirection: swiperDirection, dragging: dragging, transforms: transforms }); } }; var renderSlides = function() { var _obj; return /*#__PURE__*/ React.createElement("div", { ref: stageRef, className: classNames("".concat(classPrefix, "-inner"), (_obj = {}, _define_property(_obj, "".concat(classPrefix, "-inner-vertical"), isVertical), _define_property(_obj, "".concat(classPrefix, "-inner-horizontal"), !isVertical), _obj)), style: _object_spread({}, props.slideSize ? _define_property({}, isVertical ? 'height' : 'width', "".concat(props.slideSize, "px")) : {}) }, renderEffect()); }; return /*#__PURE__*/ React.createElement("div", _object_spread({ className: classNames(classPrefix, "".concat(classPrefix, "-canmove-").concat(direction), className), style: style, ref: swiperRef }, bind()), renderSlides(), renderIndicator()); }); Swiper.displayName = 'NutSwiper';