@seakoi/native-ui 1.0.0 → 1.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +559 -265
- package/dist/commonjs/components/base/carousel/carousel-item.js +21 -0
- package/dist/commonjs/components/base/carousel/carousel.js +489 -0
- package/dist/commonjs/components/base/carousel/index.js +36 -0
- package/dist/commonjs/components/base/carousel/style/index.js +47 -0
- package/dist/commonjs/components/base/carousel/types.js +5 -0
- package/dist/commonjs/components/base/carousel/utils.js +27 -0
- package/dist/commonjs/components/base/index.js +34 -23
- package/dist/module/components/base/carousel/carousel-item.js +16 -0
- package/dist/module/components/base/carousel/carousel.js +484 -0
- package/dist/module/components/base/carousel/index.js +9 -0
- package/dist/module/components/base/carousel/style/index.js +43 -0
- package/dist/module/components/base/carousel/types.js +3 -0
- package/dist/module/components/base/carousel/utils.js +20 -0
- package/dist/module/components/base/index.js +1 -0
- package/dist/typescript/commonjs/src/components/base/carousel/carousel-item.d.ts +4 -0
- package/dist/typescript/commonjs/src/components/base/carousel/carousel-item.d.ts.map +1 -0
- package/dist/typescript/commonjs/src/components/base/carousel/carousel.d.ts +7 -0
- package/dist/typescript/commonjs/src/components/base/carousel/carousel.d.ts.map +1 -0
- package/dist/typescript/commonjs/src/components/base/carousel/index.d.ts +7 -0
- package/dist/typescript/commonjs/src/components/base/carousel/index.d.ts.map +1 -0
- package/dist/typescript/commonjs/src/components/base/carousel/style/index.d.ts +41 -0
- package/dist/typescript/commonjs/src/components/base/carousel/style/index.d.ts.map +1 -0
- package/dist/typescript/commonjs/src/components/base/carousel/types.d.ts +113 -0
- package/dist/typescript/commonjs/src/components/base/carousel/types.d.ts.map +1 -0
- package/dist/typescript/commonjs/src/components/base/carousel/utils.d.ts +6 -0
- package/dist/typescript/commonjs/src/components/base/carousel/utils.d.ts.map +1 -0
- package/dist/typescript/commonjs/src/components/base/index.d.ts +1 -0
- package/dist/typescript/commonjs/src/components/base/index.d.ts.map +1 -1
- package/dist/typescript/module/src/components/base/carousel/carousel-item.d.ts +4 -0
- package/dist/typescript/module/src/components/base/carousel/carousel-item.d.ts.map +1 -0
- package/dist/typescript/module/src/components/base/carousel/carousel.d.ts +7 -0
- package/dist/typescript/module/src/components/base/carousel/carousel.d.ts.map +1 -0
- package/dist/typescript/module/src/components/base/carousel/index.d.ts +7 -0
- package/dist/typescript/module/src/components/base/carousel/index.d.ts.map +1 -0
- package/dist/typescript/module/src/components/base/carousel/style/index.d.ts +41 -0
- package/dist/typescript/module/src/components/base/carousel/style/index.d.ts.map +1 -0
- package/dist/typescript/module/src/components/base/carousel/types.d.ts +113 -0
- package/dist/typescript/module/src/components/base/carousel/types.d.ts.map +1 -0
- package/dist/typescript/module/src/components/base/carousel/utils.d.ts +6 -0
- package/dist/typescript/module/src/components/base/carousel/utils.d.ts.map +1 -0
- package/dist/typescript/module/src/components/base/index.d.ts +1 -0
- package/dist/typescript/module/src/components/base/index.d.ts.map +1 -1
- package/package.json +3 -2
- package/src/components/base/carousel/carousel-item.tsx +11 -0
- package/src/components/base/carousel/carousel.tsx +691 -0
- package/src/components/base/carousel/index.ts +8 -0
- package/src/components/base/carousel/style/index.ts +42 -0
- package/src/components/base/carousel/types.ts +134 -0
- package/src/components/base/carousel/utils.ts +26 -0
- package/src/components/base/index.ts +1 -0
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
|
|
3
|
+
Object.defineProperty(exports, "__esModule", {
|
|
4
|
+
value: true
|
|
5
|
+
});
|
|
6
|
+
exports.CarouselItem = void 0;
|
|
7
|
+
var _reactNative = require("react-native");
|
|
8
|
+
var _jsxRuntime = require("react/jsx-runtime");
|
|
9
|
+
const CarouselItem = ({
|
|
10
|
+
children,
|
|
11
|
+
style
|
|
12
|
+
}) => {
|
|
13
|
+
return /*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNative.View, {
|
|
14
|
+
style: [{
|
|
15
|
+
height: '100%',
|
|
16
|
+
width: '100%'
|
|
17
|
+
}, style],
|
|
18
|
+
children: children
|
|
19
|
+
});
|
|
20
|
+
};
|
|
21
|
+
exports.CarouselItem = CarouselItem;
|
|
@@ -0,0 +1,489 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
|
|
3
|
+
Object.defineProperty(exports, "__esModule", {
|
|
4
|
+
value: true
|
|
5
|
+
});
|
|
6
|
+
exports.Carousel = void 0;
|
|
7
|
+
var _lodashEs = require("lodash-es");
|
|
8
|
+
var _react = _interopRequireWildcard(require("react"));
|
|
9
|
+
var _reactNative = require("react-native");
|
|
10
|
+
var _utils = require("./utils.js");
|
|
11
|
+
var _index = require("../../../native-provider/index.js");
|
|
12
|
+
var _index2 = require("./style/index.js");
|
|
13
|
+
var _jsxRuntime = require("react/jsx-runtime");
|
|
14
|
+
function _interopRequireWildcard(e, t) { if ("function" == typeof WeakMap) var r = new WeakMap(), n = new WeakMap(); return (_interopRequireWildcard = function (e, t) { if (!t && e && e.__esModule) return e; var o, i, f = { __proto__: null, default: e }; if (null === e || "object" != typeof e && "function" != typeof e) return f; if (o = t ? n : r) { if (o.has(e)) return o.get(e); o.set(e, f); } for (const t in e) "default" !== t && {}.hasOwnProperty.call(e, t) && ((i = (o = Object.defineProperty) && Object.getOwnPropertyDescriptor(e, t)) && (i.get || i.set) ? o(f, t, i) : f[t] = e[t]); return f; })(e, t); }
|
|
15
|
+
const getBoundaryIndexRange = ({
|
|
16
|
+
total,
|
|
17
|
+
slideSize,
|
|
18
|
+
trackOffset,
|
|
19
|
+
stuckAtBoundary
|
|
20
|
+
}) => {
|
|
21
|
+
if (total <= 0) return {
|
|
22
|
+
min: 0,
|
|
23
|
+
max: 0
|
|
24
|
+
};
|
|
25
|
+
if (!stuckAtBoundary) return {
|
|
26
|
+
min: 0,
|
|
27
|
+
max: total - 1
|
|
28
|
+
};
|
|
29
|
+
const slideRatio = slideSize / 100;
|
|
30
|
+
const offsetRatio = trackOffset / 100;
|
|
31
|
+
const min = 0 + offsetRatio / (slideRatio || 1);
|
|
32
|
+
const max = total - 1 - (1 - slideRatio - offsetRatio) / (slideRatio || 1);
|
|
33
|
+
return {
|
|
34
|
+
min,
|
|
35
|
+
max
|
|
36
|
+
};
|
|
37
|
+
};
|
|
38
|
+
/**
|
|
39
|
+
* 轮播图
|
|
40
|
+
*/
|
|
41
|
+
const Carousel = exports.Carousel = /*#__PURE__*/(0, _react.forwardRef)((props, ref) => {
|
|
42
|
+
const {
|
|
43
|
+
defaultIndex = 0,
|
|
44
|
+
allowTouchMove = true,
|
|
45
|
+
autoplay = false,
|
|
46
|
+
autoplayInterval = 3000,
|
|
47
|
+
loop = false,
|
|
48
|
+
direction = 'horizontal',
|
|
49
|
+
onIndexChange,
|
|
50
|
+
indicatorProps,
|
|
51
|
+
indicator,
|
|
52
|
+
slideSize = 100,
|
|
53
|
+
trackOffset = 0,
|
|
54
|
+
stuckAtBoundary = true,
|
|
55
|
+
rubberband = true,
|
|
56
|
+
virtualOverscan = 2,
|
|
57
|
+
total: totalProp,
|
|
58
|
+
children,
|
|
59
|
+
style
|
|
60
|
+
} = props;
|
|
61
|
+
const styles = (0, _index2.useCarouselStyles)();
|
|
62
|
+
const theme = (0, _index.useTheme)();
|
|
63
|
+
const [layout, setLayout] = (0, _react.useState)({
|
|
64
|
+
width: 0,
|
|
65
|
+
height: 0
|
|
66
|
+
});
|
|
67
|
+
const [dragging, setDragging] = (0, _react.useState)(false);
|
|
68
|
+
const [animating, setAnimating] = (0, _react.useState)(false);
|
|
69
|
+
const [current, setCurrent] = (0, _react.useState)(0);
|
|
70
|
+
const position = (0, _react.useRef)(new _reactNative.Animated.Value(0));
|
|
71
|
+
const positionValueRef = (0, _react.useRef)(0);
|
|
72
|
+
const normalizedSlideSize = (0, _react.useMemo)(() => {
|
|
73
|
+
if (!Number.isFinite(slideSize)) return 100;
|
|
74
|
+
return (0, _lodashEs.clamp)(slideSize, 0, 100);
|
|
75
|
+
}, [slideSize]);
|
|
76
|
+
const normalizedTrackOffset = (0, _react.useMemo)(() => {
|
|
77
|
+
if (stuckAtBoundary) {
|
|
78
|
+
return 100 - normalizedSlideSize;
|
|
79
|
+
}
|
|
80
|
+
if (!Number.isFinite(trackOffset)) return 0;
|
|
81
|
+
return (0, _lodashEs.clamp)(trackOffset, 0, 100 - normalizedSlideSize);
|
|
82
|
+
}, [normalizedSlideSize, trackOffset, stuckAtBoundary]);
|
|
83
|
+
const total = (0, _react.useMemo)(() => {
|
|
84
|
+
const normalizedDefault = (0, _utils.getDefaultTotal)(children);
|
|
85
|
+
return totalProp ?? normalizedDefault;
|
|
86
|
+
}, [children, totalProp]);
|
|
87
|
+
const loopEnabled = loop && total > 1;
|
|
88
|
+
// 若是非全屏的轮播项,则在循环播放开启的情况下,则会在下一轮展示第一项时看见下一项为空白(或闪烁),这需要额外渲染更多的项(与offset、slideSize相关)
|
|
89
|
+
const clonesBefore = loopEnabled ? Math.max(1, normalizedSlideSize > 0 ? Math.ceil(normalizedTrackOffset / normalizedSlideSize) + 1 : 1) : 0;
|
|
90
|
+
const clonesAfter = loopEnabled ? Math.max(1, normalizedSlideSize > 0 ? Math.ceil((100 - normalizedTrackOffset - normalizedSlideSize) / normalizedSlideSize) + 1 : 1) : 0;
|
|
91
|
+
const extTotal = loopEnabled ? total + clonesBefore + clonesAfter : total;
|
|
92
|
+
const slidePixels = (0, _react.useMemo)(() => {
|
|
93
|
+
const trackSize = direction === 'horizontal' ? layout.width : layout.height;
|
|
94
|
+
if (trackSize <= 0) return 0;
|
|
95
|
+
return trackSize * (normalizedSlideSize / 100);
|
|
96
|
+
}, [direction, layout.height, layout.width, normalizedSlideSize]);
|
|
97
|
+
const trackOffsetPixels = (0, _react.useMemo)(() => {
|
|
98
|
+
const trackSize = direction === 'horizontal' ? layout.width : layout.height;
|
|
99
|
+
if (trackSize <= 0) return 0;
|
|
100
|
+
return trackSize * (normalizedTrackOffset / 100);
|
|
101
|
+
}, [direction, layout.height, layout.width, normalizedTrackOffset]);
|
|
102
|
+
const extIndexRef = (0, _react.useRef)(0);
|
|
103
|
+
const reportIndexRef = (0, _react.useRef)(null);
|
|
104
|
+
const staticSlides = (0, _react.useMemo)(() => {
|
|
105
|
+
if ((0, _utils.isRenderPropChildren)(children)) return [];
|
|
106
|
+
return (0, _utils.normalizeChildren)(children, total);
|
|
107
|
+
}, [children, total]);
|
|
108
|
+
const updateCurrent = (0, _react.useCallback)(next => {
|
|
109
|
+
const normalized = total > 0 ? (0, _lodashEs.clamp)(next, 0, total - 1) : 0;
|
|
110
|
+
setCurrent(normalized);
|
|
111
|
+
}, [total]);
|
|
112
|
+
const maybeReportIndex = (0, _react.useCallback)(index => {
|
|
113
|
+
if (!onIndexChange) return;
|
|
114
|
+
if (reportIndexRef.current === index) return;
|
|
115
|
+
reportIndexRef.current = index;
|
|
116
|
+
onIndexChange(index);
|
|
117
|
+
}, [onIndexChange]);
|
|
118
|
+
const getExtIndexFromIndex = (0, _react.useCallback)(index => {
|
|
119
|
+
if (!loopEnabled) return index;
|
|
120
|
+
return (0, _lodashEs.clamp)(index, 0, total - 1) + clonesBefore;
|
|
121
|
+
}, [clonesBefore, loopEnabled, total]);
|
|
122
|
+
const getIndexFromExtIndex = (0, _react.useCallback)(extIndex => {
|
|
123
|
+
if (!loopEnabled) return (0, _lodashEs.clamp)(extIndex, 0, total - 1);
|
|
124
|
+
if (extIndex < clonesBefore) {
|
|
125
|
+
return ((extIndex - clonesBefore) % total + total) % total;
|
|
126
|
+
}
|
|
127
|
+
if (extIndex >= clonesBefore + total) {
|
|
128
|
+
return (extIndex - clonesBefore - total) % total;
|
|
129
|
+
}
|
|
130
|
+
return extIndex - clonesBefore;
|
|
131
|
+
}, [clonesBefore, loopEnabled, total]);
|
|
132
|
+
const getTargetExtIndexForIndex = (0, _react.useCallback)(targetIndex => {
|
|
133
|
+
if (!loopEnabled) return (0, _lodashEs.clamp)(targetIndex, 0, total - 1);
|
|
134
|
+
const currentExt = extIndexRef.current;
|
|
135
|
+
const normalizedTarget = (0, _lodashEs.clamp)(targetIndex, 0, total - 1);
|
|
136
|
+
const targetExt = normalizedTarget + clonesBefore;
|
|
137
|
+
if (currentExt === clonesBefore && normalizedTarget === total - 1) return clonesBefore - 1;
|
|
138
|
+
if (currentExt === clonesBefore + total - 1 && normalizedTarget === 0) return clonesBefore + total;
|
|
139
|
+
return targetExt;
|
|
140
|
+
}, [clonesBefore, loopEnabled, total]);
|
|
141
|
+
const getBoundedPosition = (0, _react.useCallback)(nextPosition => {
|
|
142
|
+
if (loopEnabled) return nextPosition;
|
|
143
|
+
if (slidePixels <= 0) return nextPosition;
|
|
144
|
+
if (total <= 0) return 0;
|
|
145
|
+
const {
|
|
146
|
+
min,
|
|
147
|
+
max
|
|
148
|
+
} = getBoundaryIndexRange({
|
|
149
|
+
total,
|
|
150
|
+
slideSize: normalizedSlideSize,
|
|
151
|
+
trackOffset: normalizedTrackOffset,
|
|
152
|
+
stuckAtBoundary
|
|
153
|
+
});
|
|
154
|
+
const minPos = min * slidePixels;
|
|
155
|
+
const maxPos = max * slidePixels;
|
|
156
|
+
if (nextPosition < minPos) {
|
|
157
|
+
if (!rubberband) return minPos;
|
|
158
|
+
return minPos - (minPos - nextPosition) * 0.35;
|
|
159
|
+
}
|
|
160
|
+
if (nextPosition > maxPos) {
|
|
161
|
+
if (!rubberband) return maxPos;
|
|
162
|
+
return maxPos + (nextPosition - maxPos) * 0.35;
|
|
163
|
+
}
|
|
164
|
+
return nextPosition;
|
|
165
|
+
}, [loopEnabled, rubberband, slidePixels, normalizedSlideSize, stuckAtBoundary, total, normalizedTrackOffset]);
|
|
166
|
+
const animateTo = (0, _react.useCallback)((toValue, onFinished) => {
|
|
167
|
+
const listenerId = position.current.addListener(({
|
|
168
|
+
value
|
|
169
|
+
}) => {
|
|
170
|
+
positionValueRef.current = value;
|
|
171
|
+
});
|
|
172
|
+
_reactNative.Animated.timing(position.current, {
|
|
173
|
+
toValue,
|
|
174
|
+
duration: 300,
|
|
175
|
+
easing: _reactNative.Easing.out(_reactNative.Easing.cubic),
|
|
176
|
+
useNativeDriver: true
|
|
177
|
+
}).start(({
|
|
178
|
+
finished
|
|
179
|
+
}) => {
|
|
180
|
+
position.current.removeListener(listenerId);
|
|
181
|
+
if (finished) {
|
|
182
|
+
positionValueRef.current = toValue;
|
|
183
|
+
onFinished?.();
|
|
184
|
+
}
|
|
185
|
+
});
|
|
186
|
+
}, []);
|
|
187
|
+
const setPositionImmediate = (0, _react.useCallback)(toValue => {
|
|
188
|
+
position.current.stopAnimation();
|
|
189
|
+
position.current.setValue(toValue);
|
|
190
|
+
positionValueRef.current = toValue;
|
|
191
|
+
}, []);
|
|
192
|
+
const finalizeLoopJumpIfNeeded = (0, _react.useCallback)(() => {
|
|
193
|
+
if (!loopEnabled) return;
|
|
194
|
+
const currentExt = extIndexRef.current;
|
|
195
|
+
if (currentExt < clonesBefore) {
|
|
196
|
+
const targetExt = currentExt + total;
|
|
197
|
+
extIndexRef.current = targetExt;
|
|
198
|
+
if (slidePixels > 0) setPositionImmediate(targetExt * slidePixels);
|
|
199
|
+
return;
|
|
200
|
+
}
|
|
201
|
+
if (currentExt >= clonesBefore + total) {
|
|
202
|
+
const targetExt = currentExt - total;
|
|
203
|
+
extIndexRef.current = targetExt;
|
|
204
|
+
if (slidePixels > 0) setPositionImmediate(targetExt * slidePixels);
|
|
205
|
+
}
|
|
206
|
+
}, [clonesBefore, loopEnabled, setPositionImmediate, slidePixels, total]);
|
|
207
|
+
const swipeToExtIndex = (0, _react.useCallback)(targetExtIndex => {
|
|
208
|
+
if (total <= 0) return;
|
|
209
|
+
if (slidePixels <= 0) {
|
|
210
|
+
extIndexRef.current = (0, _lodashEs.clamp)(targetExtIndex, 0, extTotal - 1);
|
|
211
|
+
setAnimating(false);
|
|
212
|
+
updateCurrent(getIndexFromExtIndex(extIndexRef.current));
|
|
213
|
+
return;
|
|
214
|
+
}
|
|
215
|
+
const boundedExtIndex = (0, _lodashEs.clamp)(targetExtIndex, 0, extTotal - 1);
|
|
216
|
+
extIndexRef.current = boundedExtIndex;
|
|
217
|
+
const nextIndex = getIndexFromExtIndex(boundedExtIndex);
|
|
218
|
+
updateCurrent(nextIndex);
|
|
219
|
+
const rawPosition = boundedExtIndex * slidePixels;
|
|
220
|
+
const toPosition = getBoundedPosition(rawPosition);
|
|
221
|
+
setAnimating(true);
|
|
222
|
+
animateTo(toPosition, () => {
|
|
223
|
+
finalizeLoopJumpIfNeeded();
|
|
224
|
+
const normalizedIndex = getIndexFromExtIndex(extIndexRef.current);
|
|
225
|
+
updateCurrent(normalizedIndex);
|
|
226
|
+
maybeReportIndex(normalizedIndex);
|
|
227
|
+
setAnimating(false);
|
|
228
|
+
});
|
|
229
|
+
}, [animateTo, extTotal, finalizeLoopJumpIfNeeded, getBoundedPosition, getIndexFromExtIndex, maybeReportIndex, slidePixels, total, updateCurrent]);
|
|
230
|
+
const swipeTo = (0, _react.useCallback)(index => {
|
|
231
|
+
if (total <= 0) return;
|
|
232
|
+
const targetIndex = (0, _lodashEs.clamp)(Math.round(index), 0, total - 1);
|
|
233
|
+
const targetExtIndex = getTargetExtIndexForIndex(targetIndex);
|
|
234
|
+
swipeToExtIndex(targetExtIndex);
|
|
235
|
+
}, [getTargetExtIndexForIndex, swipeToExtIndex, total]);
|
|
236
|
+
const swipeNext = (0, _react.useCallback)(() => {
|
|
237
|
+
if (total <= 1) return;
|
|
238
|
+
let currentExt = extIndexRef.current;
|
|
239
|
+
// 若当前处于 after-clone 区域(快速连击导致 finalizeLoopJumpIfNeeded 尚未执行),
|
|
240
|
+
// 立即将位置平移回真实 item 区域(视觉内容相同,不可感知),再继续前进
|
|
241
|
+
if (clonesBefore > 0 && currentExt >= clonesBefore + total) {
|
|
242
|
+
currentExt -= total;
|
|
243
|
+
extIndexRef.current = currentExt;
|
|
244
|
+
if (slidePixels > 0) setPositionImmediate(currentExt * slidePixels);
|
|
245
|
+
}
|
|
246
|
+
swipeToExtIndex(currentExt + 1);
|
|
247
|
+
}, [clonesBefore, setPositionImmediate, slidePixels, swipeToExtIndex, total]);
|
|
248
|
+
const swipePrev = (0, _react.useCallback)(() => {
|
|
249
|
+
if (total <= 1) return;
|
|
250
|
+
let currentExt = extIndexRef.current;
|
|
251
|
+
// 若当前处于 before-clone 区域(快速连击导致 finalizeLoopJumpIfNeeded 尚未执行),
|
|
252
|
+
// 立即将位置平移回真实 item 区域(视觉内容相同,不可感知),再继续后退
|
|
253
|
+
if (clonesBefore > 0 && currentExt < clonesBefore) {
|
|
254
|
+
currentExt += total;
|
|
255
|
+
extIndexRef.current = currentExt;
|
|
256
|
+
if (slidePixels > 0) setPositionImmediate(currentExt * slidePixels);
|
|
257
|
+
}
|
|
258
|
+
swipeToExtIndex(currentExt - 1);
|
|
259
|
+
}, [clonesBefore, setPositionImmediate, slidePixels, swipeToExtIndex, total]);
|
|
260
|
+
(0, _react.useImperativeHandle)(ref, () => ({
|
|
261
|
+
swipeTo,
|
|
262
|
+
swipeNext,
|
|
263
|
+
swipePrev
|
|
264
|
+
}), [swipeNext, swipePrev, swipeTo]);
|
|
265
|
+
(0, _react.useEffect)(() => {
|
|
266
|
+
if (total <= 0) {
|
|
267
|
+
updateCurrent(0);
|
|
268
|
+
reportIndexRef.current = null;
|
|
269
|
+
extIndexRef.current = 0;
|
|
270
|
+
setPositionImmediate(0);
|
|
271
|
+
return;
|
|
272
|
+
}
|
|
273
|
+
const normalizedDefault = (0, _lodashEs.clamp)(Math.round(defaultIndex), 0, total - 1);
|
|
274
|
+
updateCurrent(normalizedDefault);
|
|
275
|
+
reportIndexRef.current = normalizedDefault;
|
|
276
|
+
const extIndex = getExtIndexFromIndex(normalizedDefault);
|
|
277
|
+
extIndexRef.current = extIndex;
|
|
278
|
+
if (slidePixels > 0) {
|
|
279
|
+
setPositionImmediate(getBoundedPosition(extIndex * slidePixels));
|
|
280
|
+
}
|
|
281
|
+
}, [defaultIndex, getExtIndexFromIndex, getBoundedPosition, setPositionImmediate, slidePixels, total, updateCurrent]);
|
|
282
|
+
(0, _react.useEffect)(() => {
|
|
283
|
+
if (slidePixels <= 0) return;
|
|
284
|
+
const extIndex = extIndexRef.current;
|
|
285
|
+
setPositionImmediate(getBoundedPosition(extIndex * slidePixels));
|
|
286
|
+
}, [getBoundedPosition, setPositionImmediate, slidePixels]);
|
|
287
|
+
|
|
288
|
+
// 每当 current 变化时,若允许自动轮播 且 开启了自动轮播,则执行轮播
|
|
289
|
+
(0, _react.useEffect)(() => {
|
|
290
|
+
if (!autoplay) return;
|
|
291
|
+
if (total <= 1) return;
|
|
292
|
+
if (dragging) return;
|
|
293
|
+
if (autoplayInterval <= 0) return;
|
|
294
|
+
const timer = setTimeout(() => {
|
|
295
|
+
if (autoplay === 'reverse') swipePrev();else swipeNext();
|
|
296
|
+
}, autoplayInterval);
|
|
297
|
+
return () => {
|
|
298
|
+
clearTimeout(timer);
|
|
299
|
+
};
|
|
300
|
+
}, [autoplay, autoplayInterval, dragging, swipeNext, swipePrev, total, current]);
|
|
301
|
+
const onLayoutTrack = (0, _react.useCallback)(e => {
|
|
302
|
+
const {
|
|
303
|
+
width,
|
|
304
|
+
height
|
|
305
|
+
} = e.nativeEvent.layout;
|
|
306
|
+
setLayout(prev => {
|
|
307
|
+
if (prev.width === width && prev.height === height) return prev;
|
|
308
|
+
return {
|
|
309
|
+
width,
|
|
310
|
+
height
|
|
311
|
+
};
|
|
312
|
+
});
|
|
313
|
+
}, []);
|
|
314
|
+
const panResponder = (0, _react.useMemo)(() => {
|
|
315
|
+
if (!allowTouchMove) return null;
|
|
316
|
+
if (total <= 1) return null;
|
|
317
|
+
if (slidePixels <= 0) return null;
|
|
318
|
+
let startPosition = 0;
|
|
319
|
+
const axis = direction === 'horizontal' ? 'x' : 'y';
|
|
320
|
+
const shouldSet = (_, gestureState) => {
|
|
321
|
+
const primary = axis === 'x' ? gestureState.dx : gestureState.dy;
|
|
322
|
+
// const secondary = axis === 'x' ? gestureState.dy : gestureState.dx;
|
|
323
|
+
if (Math.abs(primary) < 5) return false;
|
|
324
|
+
// return Math.abs(primary) >= Math.abs(secondary);
|
|
325
|
+
return true;
|
|
326
|
+
};
|
|
327
|
+
return _reactNative.PanResponder.create({
|
|
328
|
+
onMoveShouldSetPanResponder: shouldSet,
|
|
329
|
+
onPanResponderGrant: () => {
|
|
330
|
+
setDragging(true);
|
|
331
|
+
setAnimating(false);
|
|
332
|
+
position.current.stopAnimation();
|
|
333
|
+
startPosition = positionValueRef.current;
|
|
334
|
+
},
|
|
335
|
+
onPanResponderMove: (_evt, gestureState) => {
|
|
336
|
+
const delta = axis === 'x' ? gestureState.dx : gestureState.dy;
|
|
337
|
+
let nextPosition = startPosition - delta;
|
|
338
|
+
|
|
339
|
+
// loop 开启时,拖动到最外层有效克隆中心之外则循环偏移,保证视口内始终有内容
|
|
340
|
+
// minPos/maxPos 是 outermost before/after clone 的中心位置,由 clonesBefore 公式保证此范围内视口始终被覆盖
|
|
341
|
+
if (extTotal > total && slidePixels > 0) {
|
|
342
|
+
const totalPixels = total * slidePixels;
|
|
343
|
+
const minPos = (clonesBefore - 1) * slidePixels;
|
|
344
|
+
const maxPos = (clonesBefore + total) * slidePixels;
|
|
345
|
+
while (nextPosition < minPos) {
|
|
346
|
+
nextPosition += totalPixels;
|
|
347
|
+
startPosition += totalPixels;
|
|
348
|
+
}
|
|
349
|
+
while (nextPosition > maxPos) {
|
|
350
|
+
nextPosition -= totalPixels;
|
|
351
|
+
startPosition -= totalPixels;
|
|
352
|
+
}
|
|
353
|
+
}
|
|
354
|
+
const bounded = getBoundedPosition(nextPosition);
|
|
355
|
+
position.current.setValue(bounded);
|
|
356
|
+
positionValueRef.current = bounded;
|
|
357
|
+
const nextExtIndex = (0, _lodashEs.clamp)(Math.round(bounded / slidePixels), 0, extTotal - 1);
|
|
358
|
+
extIndexRef.current = nextExtIndex;
|
|
359
|
+
updateCurrent(getIndexFromExtIndex(nextExtIndex));
|
|
360
|
+
},
|
|
361
|
+
onPanResponderRelease: (_evt, gestureState) => {
|
|
362
|
+
setDragging(false);
|
|
363
|
+
const velocity = axis === 'x' ? gestureState.vx : gestureState.vy;
|
|
364
|
+
position.current.stopAnimation(value => {
|
|
365
|
+
const baseIndex = value / slidePixels;
|
|
366
|
+
const projectedIndex = (value - velocity * 2000) / slidePixels;
|
|
367
|
+
const minAdj = Math.floor(baseIndex);
|
|
368
|
+
const maxAdj = minAdj + 1;
|
|
369
|
+
const rounded = Math.round(projectedIndex);
|
|
370
|
+
const adjacent = (0, _lodashEs.clamp)(rounded, minAdj, maxAdj);
|
|
371
|
+
swipeToExtIndex(adjacent);
|
|
372
|
+
});
|
|
373
|
+
},
|
|
374
|
+
onPanResponderTerminate: () => {
|
|
375
|
+
setDragging(false);
|
|
376
|
+
swipeToExtIndex(extIndexRef.current);
|
|
377
|
+
},
|
|
378
|
+
onPanResponderTerminationRequest: () => false,
|
|
379
|
+
onShouldBlockNativeResponder: () => true
|
|
380
|
+
});
|
|
381
|
+
}, [allowTouchMove, clonesBefore, direction, extTotal, getBoundedPosition, getIndexFromExtIndex, slidePixels, swipeToExtIndex, total, updateCurrent]);
|
|
382
|
+
const trackTransformStyle = (0, _react.useMemo)(() => {
|
|
383
|
+
const translate = _reactNative.Animated.add(_reactNative.Animated.multiply(position.current, -1), trackOffsetPixels);
|
|
384
|
+
return direction === 'horizontal' ? {
|
|
385
|
+
transform: [{
|
|
386
|
+
translateX: translate
|
|
387
|
+
}]
|
|
388
|
+
} : {
|
|
389
|
+
transform: [{
|
|
390
|
+
translateY: translate
|
|
391
|
+
}]
|
|
392
|
+
};
|
|
393
|
+
}, [direction, trackOffsetPixels]);
|
|
394
|
+
const renderSlides = (0, _react.useCallback)(() => {
|
|
395
|
+
if (total <= 0) return null;
|
|
396
|
+
if (slidePixels <= 0) return null;
|
|
397
|
+
const isVirtual = (0, _utils.isRenderPropChildren)(children) && typeof totalProp === 'number';
|
|
398
|
+
const effectiveOverscan = isVirtual ? Math.max(virtualOverscan, dragging || animating ? 1 : 0) : virtualOverscan;
|
|
399
|
+
const currentExtIndex = extIndexRef.current;
|
|
400
|
+
const start = isVirtual ? (0, _lodashEs.clamp)(currentExtIndex - effectiveOverscan, 0, extTotal - 1) : 0;
|
|
401
|
+
const end = isVirtual ? (0, _lodashEs.clamp)(currentExtIndex + effectiveOverscan, 0, extTotal - 1) : extTotal - 1;
|
|
402
|
+
const leadingSize = start * slidePixels;
|
|
403
|
+
const trailingSize = (extTotal - end - 1) * slidePixels;
|
|
404
|
+
const leadSpacerStyle = direction === 'horizontal' ? {
|
|
405
|
+
width: leadingSize
|
|
406
|
+
} : {
|
|
407
|
+
height: leadingSize
|
|
408
|
+
};
|
|
409
|
+
const trailSpacerStyle = direction === 'horizontal' ? {
|
|
410
|
+
width: trailingSize
|
|
411
|
+
} : {
|
|
412
|
+
height: trailingSize
|
|
413
|
+
};
|
|
414
|
+
const slideWrapperBaseStyle = direction === 'horizontal' ? {
|
|
415
|
+
width: slidePixels,
|
|
416
|
+
height: '100%'
|
|
417
|
+
} : {
|
|
418
|
+
height: slidePixels,
|
|
419
|
+
width: '100%'
|
|
420
|
+
};
|
|
421
|
+
const getElementForExtIndex = extIndex => {
|
|
422
|
+
const logicalIndex = loopEnabled ? getIndexFromExtIndex(extIndex) : (0, _lodashEs.clamp)(extIndex, 0, total - 1);
|
|
423
|
+
if ((0, _utils.isRenderPropChildren)(children)) return children(logicalIndex);
|
|
424
|
+
return staticSlides[logicalIndex];
|
|
425
|
+
};
|
|
426
|
+
const slideElements = [];
|
|
427
|
+
for (let extIndex = start; extIndex <= end; extIndex += 1) {
|
|
428
|
+
const element = getElementForExtIndex(extIndex);
|
|
429
|
+
if (!element) continue;
|
|
430
|
+
slideElements.push(/*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNative.View, {
|
|
431
|
+
style: [styles.slide, slideWrapperBaseStyle],
|
|
432
|
+
children: element
|
|
433
|
+
}, extIndex));
|
|
434
|
+
}
|
|
435
|
+
const leadSpacer = isVirtual && leadingSize > 0 ? /*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNative.View, {
|
|
436
|
+
style: leadSpacerStyle
|
|
437
|
+
}) : null;
|
|
438
|
+
const trailSpacer = isVirtual && trailingSize > 0 ? /*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNative.View, {
|
|
439
|
+
style: trailSpacerStyle
|
|
440
|
+
}) : null;
|
|
441
|
+
return /*#__PURE__*/(0, _jsxRuntime.jsxs)(_jsxRuntime.Fragment, {
|
|
442
|
+
children: [leadSpacer, slideElements, trailSpacer]
|
|
443
|
+
});
|
|
444
|
+
}, [children, direction, extTotal, getIndexFromExtIndex, dragging, animating, loopEnabled, slidePixels, staticSlides, styles.slide, total, totalProp, virtualOverscan]);
|
|
445
|
+
const renderIndicator = (0, _react.useCallback)(() => {
|
|
446
|
+
if (indicator === false) return null;
|
|
447
|
+
if (total <= 1) return null;
|
|
448
|
+
if (typeof indicator === 'function') {
|
|
449
|
+
return indicator(total, current);
|
|
450
|
+
}
|
|
451
|
+
const activeColor = indicatorProps?.color ?? theme.palette.brand7;
|
|
452
|
+
const inactiveColor = theme.palette.fontGray5;
|
|
453
|
+
return /*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNative.View, {
|
|
454
|
+
style: _reactNative.StyleSheet.flatten([styles.indicatorContainer, indicatorProps?.style]),
|
|
455
|
+
children: Array.from({
|
|
456
|
+
length: total
|
|
457
|
+
}, (_, i) => {
|
|
458
|
+
const isActive = i === current;
|
|
459
|
+
return /*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNative.View, {
|
|
460
|
+
style: _reactNative.StyleSheet.flatten([styles.indicatorDot, {
|
|
461
|
+
backgroundColor: isActive ? activeColor : inactiveColor
|
|
462
|
+
}, isActive && styles.indicatorDotActive])
|
|
463
|
+
}, i);
|
|
464
|
+
})
|
|
465
|
+
});
|
|
466
|
+
}, [current, indicator, indicatorProps?.color, indicatorProps?.style, styles.indicatorContainer, styles.indicatorDot, styles.indicatorDotActive, theme.palette.brand7, theme.palette.fontGray5, total]);
|
|
467
|
+
const trackInnerStyle = (0, _react.useMemo)(() => {
|
|
468
|
+
return direction === 'horizontal' ? {
|
|
469
|
+
flexDirection: 'row',
|
|
470
|
+
height: '100%'
|
|
471
|
+
} : {
|
|
472
|
+
flexDirection: 'column',
|
|
473
|
+
width: '100%'
|
|
474
|
+
};
|
|
475
|
+
}, [direction]);
|
|
476
|
+
return /*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNative.View, {
|
|
477
|
+
style: _reactNative.StyleSheet.flatten([styles.container, style]),
|
|
478
|
+
children: /*#__PURE__*/(0, _jsxRuntime.jsxs)(_reactNative.View, {
|
|
479
|
+
style: styles.track,
|
|
480
|
+
onLayout: onLayoutTrack,
|
|
481
|
+
...panResponder?.panHandlers,
|
|
482
|
+
children: [/*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNative.Animated.View, {
|
|
483
|
+
style: _reactNative.StyleSheet.flatten([styles.trackInner, trackInnerStyle, trackTransformStyle]),
|
|
484
|
+
children: renderSlides()
|
|
485
|
+
}), renderIndicator()]
|
|
486
|
+
})
|
|
487
|
+
});
|
|
488
|
+
});
|
|
489
|
+
Carousel.displayName = 'Carousel';
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
|
|
3
|
+
Object.defineProperty(exports, "__esModule", {
|
|
4
|
+
value: true
|
|
5
|
+
});
|
|
6
|
+
var _exportNames = {
|
|
7
|
+
Carousel: true
|
|
8
|
+
};
|
|
9
|
+
exports.Carousel = void 0;
|
|
10
|
+
var _carousel = require("./carousel.js");
|
|
11
|
+
Object.keys(_carousel).forEach(function (key) {
|
|
12
|
+
if (key === "default" || key === "__esModule") return;
|
|
13
|
+
if (Object.prototype.hasOwnProperty.call(_exportNames, key)) return;
|
|
14
|
+
if (key in exports && exports[key] === _carousel[key]) return;
|
|
15
|
+
Object.defineProperty(exports, key, {
|
|
16
|
+
enumerable: true,
|
|
17
|
+
get: function () {
|
|
18
|
+
return _carousel[key];
|
|
19
|
+
}
|
|
20
|
+
});
|
|
21
|
+
});
|
|
22
|
+
var _carouselItem = require("./carousel-item.js");
|
|
23
|
+
Object.keys(_carouselItem).forEach(function (key) {
|
|
24
|
+
if (key === "default" || key === "__esModule") return;
|
|
25
|
+
if (Object.prototype.hasOwnProperty.call(_exportNames, key)) return;
|
|
26
|
+
if (key in exports && exports[key] === _carouselItem[key]) return;
|
|
27
|
+
Object.defineProperty(exports, key, {
|
|
28
|
+
enumerable: true,
|
|
29
|
+
get: function () {
|
|
30
|
+
return _carouselItem[key];
|
|
31
|
+
}
|
|
32
|
+
});
|
|
33
|
+
});
|
|
34
|
+
const Carousel = exports.Carousel = Object.assign(_carousel.Carousel, {
|
|
35
|
+
Item: _carouselItem.CarouselItem
|
|
36
|
+
});
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
|
|
3
|
+
Object.defineProperty(exports, "__esModule", {
|
|
4
|
+
value: true
|
|
5
|
+
});
|
|
6
|
+
exports.useCarouselStyles = void 0;
|
|
7
|
+
var _index = require("../../../../native-provider/index.js");
|
|
8
|
+
const useCarouselStyles = exports.useCarouselStyles = (0, _index.createThemedStyles)(theme => ({
|
|
9
|
+
container: {
|
|
10
|
+
overflow: 'hidden',
|
|
11
|
+
height: '100%',
|
|
12
|
+
width: '100%'
|
|
13
|
+
},
|
|
14
|
+
track: {
|
|
15
|
+
flex: 1,
|
|
16
|
+
overflow: 'hidden'
|
|
17
|
+
},
|
|
18
|
+
trackInner: {
|
|
19
|
+
flexGrow: 0,
|
|
20
|
+
flexShrink: 0
|
|
21
|
+
},
|
|
22
|
+
slide: {
|
|
23
|
+
flexGrow: 0,
|
|
24
|
+
flexShrink: 0,
|
|
25
|
+
overflow: 'hidden'
|
|
26
|
+
},
|
|
27
|
+
indicatorContainer: {
|
|
28
|
+
position: 'absolute',
|
|
29
|
+
left: 0,
|
|
30
|
+
right: 0,
|
|
31
|
+
bottom: 8,
|
|
32
|
+
flexDirection: 'row',
|
|
33
|
+
justifyContent: 'center',
|
|
34
|
+
alignItems: 'center',
|
|
35
|
+
gap: 6
|
|
36
|
+
},
|
|
37
|
+
indicatorDot: {
|
|
38
|
+
width: 6,
|
|
39
|
+
height: 6,
|
|
40
|
+
borderRadius: theme.roundness.full,
|
|
41
|
+
backgroundColor: theme.palette.fontGray5,
|
|
42
|
+
opacity: 0.6
|
|
43
|
+
},
|
|
44
|
+
indicatorDotActive: {
|
|
45
|
+
opacity: 1
|
|
46
|
+
}
|
|
47
|
+
}));
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
|
|
3
|
+
Object.defineProperty(exports, "__esModule", {
|
|
4
|
+
value: true
|
|
5
|
+
});
|
|
6
|
+
exports.normalizeChildren = exports.isRenderPropChildren = exports.getDefaultTotal = void 0;
|
|
7
|
+
var _react = require("react");
|
|
8
|
+
const isRenderPropChildren = children => {
|
|
9
|
+
return typeof children === 'function';
|
|
10
|
+
};
|
|
11
|
+
exports.isRenderPropChildren = isRenderPropChildren;
|
|
12
|
+
const normalizeChildren = (children, total) => {
|
|
13
|
+
if (!children) return [];
|
|
14
|
+
if (isRenderPropChildren(children)) {
|
|
15
|
+
return Array.from({
|
|
16
|
+
length: total
|
|
17
|
+
}, (_, i) => children(i));
|
|
18
|
+
}
|
|
19
|
+
return _react.Children.toArray(children);
|
|
20
|
+
};
|
|
21
|
+
exports.normalizeChildren = normalizeChildren;
|
|
22
|
+
const getDefaultTotal = children => {
|
|
23
|
+
if (!children) return 0;
|
|
24
|
+
if (isRenderPropChildren(children)) return 0;
|
|
25
|
+
return Array.isArray(children) ? children.length : 1;
|
|
26
|
+
};
|
|
27
|
+
exports.getDefaultTotal = getDefaultTotal;
|