@seakoi/native-ui 1.1.2 → 1.2.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 +24 -0
- package/dist/commonjs/components/base/carousel/carousel-indicator.js +56 -0
- package/dist/commonjs/components/base/carousel/carousel-slides.js +140 -0
- package/dist/commonjs/components/base/carousel/carousel.js +114 -122
- package/dist/commonjs/components/base/carousel/hooks/index.js +0 -14
- package/dist/commonjs/components/base/carousel/hooks/use-carousel-index.js +16 -13
- package/dist/commonjs/components/base/carousel/hooks/use-carousel-lifecycle.js +6 -2
- package/dist/commonjs/components/base/carousel/hooks/use-carousel-pan-responder.js +40 -12
- package/dist/commonjs/components/base/carousel/hooks/use-carousel-position.js +6 -2
- package/dist/commonjs/components/base/carousel/index.js +1 -15
- package/dist/commonjs/components/base/carousel/style/index.js +12 -0
- package/dist/commonjs/components/base/date-picker/date-picker.js +56 -44
- package/dist/commonjs/components/base/date-picker/date-range-picker.js +142 -50
- package/dist/commonjs/components/base/date-picker/style/index.js +15 -0
- package/dist/commonjs/components/base/date-picker-view/date-picker-view.js +19 -53
- package/dist/commonjs/components/base/date-picker-view/index.js +0 -22
- package/dist/commonjs/components/base/index.js +30 -8
- package/dist/commonjs/components/base/picker/index.js +26 -4
- package/dist/commonjs/components/base/picker/picker-content.js +60 -0
- package/dist/commonjs/components/base/picker/picker-context.js +9 -0
- package/dist/commonjs/components/base/picker/picker-field.js +37 -0
- package/dist/commonjs/components/base/picker/picker.js +22 -96
- package/dist/commonjs/components/base/picker/style/index.js +1 -3
- package/dist/commonjs/components/base/picker-backup/base-picker-container.js +50 -0
- package/dist/commonjs/components/base/picker-backup/index.js +27 -0
- package/dist/commonjs/components/base/picker-backup/picker-backup.js +75 -0
- package/dist/commonjs/components/base/picker-backup/picker-copy.js +106 -0
- package/dist/commonjs/components/base/{picker → picker-backup}/picker-trigger.js +5 -5
- package/dist/commonjs/components/base/picker-backup/style/index.js +19 -0
- package/dist/commonjs/components/base/picker-backup/utils.js +53 -0
- package/dist/commonjs/components/base/picker-view/picker-view-column.js +15 -0
- package/dist/commonjs/components/base/picker-view/picker-view.js +4 -4
- package/dist/commonjs/components/base/tag/index.js +20 -0
- package/dist/commonjs/components/base/tag/style/index.js +89 -0
- package/dist/commonjs/components/base/tag/tag-context.js +12 -0
- package/dist/commonjs/components/base/tag/tag-group.js +35 -0
- package/dist/commonjs/components/base/tag/tag.js +47 -0
- package/dist/commonjs/components/base/tag/types.js +5 -0
- package/dist/module/components/base/carousel/carousel-indicator.js +51 -0
- package/dist/module/components/base/carousel/carousel-slides.js +135 -0
- package/dist/module/components/base/carousel/carousel.js +116 -124
- package/dist/module/components/base/carousel/hooks/index.js +0 -2
- package/dist/module/components/base/carousel/hooks/use-carousel-index.js +15 -11
- package/dist/module/components/base/carousel/hooks/use-carousel-lifecycle.js +6 -2
- package/dist/module/components/base/carousel/hooks/use-carousel-pan-responder.js +40 -11
- package/dist/module/components/base/carousel/hooks/use-carousel-position.js +5 -1
- package/dist/module/components/base/carousel/index.js +1 -5
- package/dist/module/components/base/carousel/style/index.js +12 -0
- package/dist/module/components/base/date-picker/date-picker.js +60 -48
- package/dist/module/components/base/date-picker/date-range-picker.js +146 -54
- package/dist/module/components/base/date-picker/style/index.js +11 -0
- package/dist/module/components/base/date-picker-view/date-picker-view.js +23 -57
- package/dist/module/components/base/date-picker-view/index.js +1 -3
- package/dist/module/components/base/index.js +2 -0
- package/dist/module/components/base/picker/index.js +9 -1
- package/dist/module/components/base/picker/picker-content.js +54 -0
- package/dist/module/components/base/picker/picker-context.js +4 -0
- package/dist/module/components/base/picker/picker-field.js +32 -0
- package/dist/module/components/base/picker/picker.js +25 -99
- package/dist/module/components/base/picker/style/index.js +1 -3
- package/dist/module/components/base/picker-backup/base-picker-container.js +44 -0
- package/dist/module/components/base/picker-backup/index.js +4 -0
- package/dist/module/components/base/picker-backup/picker-backup.js +69 -0
- package/dist/module/components/base/picker-backup/picker-copy.js +101 -0
- package/dist/module/components/base/{picker → picker-backup}/picker-trigger.js +2 -2
- package/dist/module/components/base/picker-backup/style/index.js +15 -0
- package/dist/module/components/base/picker-backup/utils.js +48 -0
- package/dist/module/components/base/picker-view/picker-view-column.js +15 -0
- package/dist/module/components/base/picker-view/picker-view.js +4 -4
- package/dist/module/components/base/tag/index.js +5 -0
- package/dist/module/components/base/tag/style/index.js +85 -0
- package/dist/module/components/base/tag/tag-context.js +8 -0
- package/dist/module/components/base/tag/tag-group.js +29 -0
- package/dist/module/components/base/tag/tag.js +41 -0
- package/dist/module/components/base/tag/types.js +3 -0
- package/dist/typescript/components/base/carousel/carousel-indicator.d.ts +42 -0
- package/dist/typescript/components/base/carousel/carousel-indicator.d.ts.map +1 -0
- package/dist/typescript/components/base/carousel/carousel-slides.d.ts +49 -0
- package/dist/typescript/components/base/carousel/carousel-slides.d.ts.map +1 -0
- package/dist/typescript/components/base/carousel/carousel.d.ts +16 -11
- package/dist/typescript/components/base/carousel/carousel.d.ts.map +1 -1
- package/dist/typescript/components/base/carousel/hooks/index.d.ts +0 -2
- package/dist/typescript/components/base/carousel/hooks/index.d.ts.map +1 -1
- package/dist/typescript/components/base/carousel/hooks/use-carousel-index.d.ts.map +1 -1
- package/dist/typescript/components/base/carousel/hooks/use-carousel-lifecycle.d.ts.map +1 -1
- package/dist/typescript/components/base/carousel/hooks/use-carousel-pan-responder.d.ts.map +1 -1
- package/dist/typescript/components/base/carousel/hooks/use-carousel-position.d.ts.map +1 -1
- package/dist/typescript/components/base/carousel/index.d.ts +1 -4
- package/dist/typescript/components/base/carousel/index.d.ts.map +1 -1
- package/dist/typescript/components/base/carousel/style/index.d.ts +12 -0
- package/dist/typescript/components/base/carousel/style/index.d.ts.map +1 -1
- package/dist/typescript/components/base/carousel/types.d.ts +8 -17
- package/dist/typescript/components/base/carousel/types.d.ts.map +1 -1
- package/dist/typescript/components/base/date-picker/date-picker.d.ts +4 -2
- package/dist/typescript/components/base/date-picker/date-picker.d.ts.map +1 -1
- package/dist/typescript/components/base/date-picker/date-range-picker.d.ts +12 -3
- package/dist/typescript/components/base/date-picker/date-range-picker.d.ts.map +1 -1
- package/dist/typescript/components/base/date-picker/style/index.d.ts +9 -0
- package/dist/typescript/components/base/date-picker/style/index.d.ts.map +1 -0
- package/dist/typescript/components/base/date-picker-view/date-picker-view.d.ts +1 -6
- package/dist/typescript/components/base/date-picker-view/date-picker-view.d.ts.map +1 -1
- package/dist/typescript/components/base/date-picker-view/index.d.ts +0 -2
- package/dist/typescript/components/base/date-picker-view/index.d.ts.map +1 -1
- package/dist/typescript/components/base/date-picker-view/types.d.ts +1 -1
- package/dist/typescript/components/base/index.d.ts +2 -0
- package/dist/typescript/components/base/index.d.ts.map +1 -1
- package/dist/typescript/components/base/picker/index.d.ts +7 -1
- package/dist/typescript/components/base/picker/index.d.ts.map +1 -1
- package/dist/typescript/components/base/picker/picker-content.d.ts +15 -0
- package/dist/typescript/components/base/picker/picker-content.d.ts.map +1 -0
- package/dist/typescript/components/base/picker/picker-context.d.ts +18 -0
- package/dist/typescript/components/base/picker/picker-context.d.ts.map +1 -0
- package/dist/typescript/components/base/picker/picker-field.d.ts +10 -0
- package/dist/typescript/components/base/picker/picker-field.d.ts.map +1 -0
- package/dist/typescript/components/base/picker/picker.d.ts +13 -11
- package/dist/typescript/components/base/picker/picker.d.ts.map +1 -1
- package/dist/typescript/components/base/picker/style/index.d.ts +0 -2
- package/dist/typescript/components/base/picker/style/index.d.ts.map +1 -1
- package/dist/typescript/components/base/picker-backup/base-picker-container.d.ts +15 -0
- package/dist/typescript/components/base/picker-backup/base-picker-container.d.ts.map +1 -0
- package/dist/typescript/components/base/picker-backup/index.d.ts +3 -0
- package/dist/typescript/components/base/picker-backup/index.d.ts.map +1 -0
- package/dist/typescript/components/base/picker-backup/picker-backup.d.ts +26 -0
- package/dist/typescript/components/base/picker-backup/picker-backup.d.ts.map +1 -0
- package/dist/typescript/components/base/picker-backup/picker-copy.d.ts +13 -0
- package/dist/typescript/components/base/picker-backup/picker-copy.d.ts.map +1 -0
- package/dist/typescript/components/base/picker-backup/picker-trigger.d.ts.map +1 -0
- package/dist/typescript/components/base/picker-backup/style/index.d.ts +13 -0
- package/dist/typescript/components/base/picker-backup/style/index.d.ts.map +1 -0
- package/dist/typescript/components/base/picker-backup/utils.d.ts +8 -0
- package/dist/typescript/components/base/picker-backup/utils.d.ts.map +1 -0
- package/dist/typescript/components/base/picker-view/picker-view-column.d.ts.map +1 -1
- package/dist/typescript/components/base/picker-view/utils/picker.d.ts +3 -3
- package/dist/typescript/components/base/picker-view/utils/picker.d.ts.map +1 -1
- package/dist/typescript/components/base/tag/index.d.ts +5 -0
- package/dist/typescript/components/base/tag/index.d.ts.map +1 -0
- package/dist/typescript/components/base/tag/style/index.d.ts +61 -0
- package/dist/typescript/components/base/tag/style/index.d.ts.map +1 -0
- package/dist/typescript/components/base/tag/tag-context.d.ts +3 -0
- package/dist/typescript/components/base/tag/tag-context.d.ts.map +1 -0
- package/dist/typescript/components/base/tag/tag-group.d.ts +4 -0
- package/dist/typescript/components/base/tag/tag-group.d.ts.map +1 -0
- package/dist/typescript/components/base/tag/tag.d.ts +4 -0
- package/dist/typescript/components/base/tag/tag.d.ts.map +1 -0
- package/dist/typescript/components/base/tag/types.d.ts +48 -0
- package/dist/typescript/components/base/tag/types.d.ts.map +1 -0
- package/package.json +12 -4
- package/src/components/base/carousel/carousel-indicator.tsx +80 -0
- package/src/components/base/carousel/carousel-slides.tsx +177 -0
- package/src/components/base/carousel/carousel.tsx +108 -118
- package/src/components/base/carousel/hooks/index.ts +0 -2
- package/src/components/base/carousel/hooks/use-carousel-index.ts +13 -9
- package/src/components/base/carousel/hooks/use-carousel-lifecycle.ts +4 -3
- package/src/components/base/carousel/hooks/use-carousel-pan-responder.ts +40 -16
- package/src/components/base/carousel/hooks/use-carousel-position.ts +4 -1
- package/src/components/base/carousel/index.ts +1 -3
- package/src/components/base/carousel/style/index.ts +12 -0
- package/src/components/base/carousel/types.ts +8 -21
- package/src/components/base/date-picker/date-picker.tsx +64 -61
- package/src/components/base/date-picker/date-range-picker.tsx +178 -70
- package/src/components/base/date-picker/style/index.ts +10 -0
- package/src/components/base/date-picker-view/date-picker-view.tsx +21 -68
- package/src/components/base/date-picker-view/index.ts +0 -2
- package/src/components/base/date-picker-view/types.ts +1 -1
- package/src/components/base/index.ts +2 -0
- package/src/components/base/picker/index.ts +11 -1
- package/src/components/base/picker/picker-content.tsx +75 -0
- package/src/components/base/picker/picker-context.ts +19 -0
- package/src/components/base/picker/picker-field.tsx +50 -0
- package/src/components/base/picker/picker.tsx +38 -114
- package/src/components/base/picker/style/index.ts +0 -2
- package/src/components/base/picker-backup/base-picker-container.tsx +55 -0
- package/src/components/base/picker-backup/index.ts +2 -0
- package/src/components/base/picker-backup/picker-backup.tsx +110 -0
- package/src/components/base/picker-backup/picker-copy.tsx +125 -0
- package/src/components/base/{picker → picker-backup}/picker-trigger.tsx +2 -2
- package/src/components/base/picker-backup/style/index.ts +14 -0
- package/src/components/base/picker-backup/utils.ts +62 -0
- package/src/components/base/picker-view/picker-view-column.tsx +20 -0
- package/src/components/base/picker-view/picker-view.tsx +4 -4
- package/src/components/base/picker-view/utils/picker.ts +3 -5
- package/src/components/base/tag/index.ts +5 -0
- package/src/components/base/tag/style/index.tsx +84 -0
- package/src/components/base/tag/tag-context.ts +9 -0
- package/src/components/base/tag/tag-group.tsx +31 -0
- package/src/components/base/tag/tag.tsx +50 -0
- package/src/components/base/tag/types.ts +71 -0
- package/dist/commonjs/components/base/carousel/carousel-item.js +0 -45
- package/dist/commonjs/components/base/carousel/constants.js +0 -25
- package/dist/commonjs/components/base/carousel/hooks/use-carousel-indicator.js +0 -63
- package/dist/commonjs/components/base/carousel/hooks/use-carousel-slides.js +0 -95
- package/dist/commonjs/components/base/carousel/utils.js +0 -63
- package/dist/commonjs/components/base/date-picker-view/date-range-picker-view.js +0 -145
- package/dist/commonjs/components/base/date-picker-view/date-time-picker.js +0 -39
- package/dist/module/components/base/carousel/carousel-item.js +0 -40
- package/dist/module/components/base/carousel/constants.js +0 -21
- package/dist/module/components/base/carousel/hooks/use-carousel-indicator.js +0 -58
- package/dist/module/components/base/carousel/hooks/use-carousel-slides.js +0 -90
- package/dist/module/components/base/carousel/utils.js +0 -55
- package/dist/module/components/base/date-picker-view/date-range-picker-view.js +0 -138
- package/dist/module/components/base/date-picker-view/date-time-picker.js +0 -34
- package/dist/typescript/components/base/carousel/carousel-item.d.ts +0 -26
- package/dist/typescript/components/base/carousel/carousel-item.d.ts.map +0 -1
- package/dist/typescript/components/base/carousel/constants.d.ts +0 -17
- package/dist/typescript/components/base/carousel/constants.d.ts.map +0 -1
- package/dist/typescript/components/base/carousel/hooks/use-carousel-indicator.d.ts +0 -37
- package/dist/typescript/components/base/carousel/hooks/use-carousel-indicator.d.ts.map +0 -1
- package/dist/typescript/components/base/carousel/hooks/use-carousel-slides.d.ts +0 -51
- package/dist/typescript/components/base/carousel/hooks/use-carousel-slides.d.ts.map +0 -1
- package/dist/typescript/components/base/carousel/utils.d.ts +0 -25
- package/dist/typescript/components/base/carousel/utils.d.ts.map +0 -1
- package/dist/typescript/components/base/date-picker-view/date-range-picker-view.d.ts +0 -26
- package/dist/typescript/components/base/date-picker-view/date-range-picker-view.d.ts.map +0 -1
- package/dist/typescript/components/base/date-picker-view/date-time-picker.d.ts +0 -3
- package/dist/typescript/components/base/date-picker-view/date-time-picker.d.ts.map +0 -1
- package/dist/typescript/components/base/picker/picker-trigger.d.ts.map +0 -1
- package/src/components/base/carousel/carousel-item.tsx +0 -35
- package/src/components/base/carousel/constants.ts +0 -19
- package/src/components/base/carousel/hooks/use-carousel-indicator.tsx +0 -84
- package/src/components/base/carousel/hooks/use-carousel-slides.tsx +0 -131
- package/src/components/base/carousel/utils.ts +0 -55
- package/src/components/base/date-picker-view/date-range-picker-view.tsx +0 -191
- package/src/components/base/date-picker-view/date-time-picker.tsx +0 -34
- /package/dist/typescript/components/base/{picker → picker-backup}/picker-trigger.d.ts +0 -0
|
@@ -4,32 +4,36 @@ import {
|
|
|
4
4
|
Animated,
|
|
5
5
|
type LayoutChangeEvent,
|
|
6
6
|
type StyleProp,
|
|
7
|
-
StyleSheet,
|
|
8
7
|
View,
|
|
9
8
|
type ViewStyle,
|
|
10
9
|
} from 'react-native';
|
|
11
10
|
|
|
12
11
|
import { useTheme } from '#native-provider';
|
|
13
12
|
|
|
13
|
+
import { CarouselIndicator } from './carousel-indicator';
|
|
14
|
+
import { CarouselSlides } from './carousel-slides';
|
|
14
15
|
import {
|
|
15
16
|
useCarouselAutoplay,
|
|
16
17
|
useCarouselIndex,
|
|
17
|
-
useCarouselIndicator,
|
|
18
18
|
useCarouselLifecycle,
|
|
19
19
|
useCarouselPanResponder,
|
|
20
20
|
useCarouselPosition,
|
|
21
|
-
useCarouselSlides,
|
|
22
21
|
useCarouselSwipe,
|
|
23
22
|
} from './hooks';
|
|
24
23
|
import { useCarouselStyles } from './style';
|
|
25
24
|
import type { CarouselProps } from './types';
|
|
26
|
-
import { getDefaultTotal, isRenderPropChildren } from './utils';
|
|
27
25
|
|
|
28
26
|
interface LayoutState {
|
|
29
27
|
width: number;
|
|
30
28
|
height: number;
|
|
31
29
|
}
|
|
32
30
|
|
|
31
|
+
/**
|
|
32
|
+
* 虚拟渲染触发阈值
|
|
33
|
+
* 当轮播项数量超过此值时启用虚拟渲染优化
|
|
34
|
+
*/
|
|
35
|
+
const VIRTUAL_RENDER_THRESHOLD = 10;
|
|
36
|
+
|
|
33
37
|
/**
|
|
34
38
|
* 轮播图组件
|
|
35
39
|
*
|
|
@@ -38,26 +42,31 @@ interface LayoutState {
|
|
|
38
42
|
* @example
|
|
39
43
|
* ```tsx
|
|
40
44
|
* // 基础用法
|
|
41
|
-
* <Carousel
|
|
42
|
-
*
|
|
43
|
-
* <
|
|
44
|
-
*
|
|
45
|
+
* <Carousel
|
|
46
|
+
* data={[1, 2, 3]}
|
|
47
|
+
* renderItem={(item) => <View><Text>{item}</Text></View>}
|
|
48
|
+
* />
|
|
45
49
|
*
|
|
46
50
|
* // 循环播放和自动轮播
|
|
47
|
-
* <Carousel
|
|
48
|
-
* {items
|
|
49
|
-
* </
|
|
51
|
+
* <Carousel
|
|
52
|
+
* data={items}
|
|
53
|
+
* renderItem={(item) => <View>{item.content}</View>}
|
|
54
|
+
* loop
|
|
55
|
+
* autoplay
|
|
56
|
+
* autoplayInterval={3000}
|
|
57
|
+
* />
|
|
50
58
|
*
|
|
51
59
|
* // 虚拟渲染(大量数据)
|
|
52
|
-
* <Carousel
|
|
53
|
-
* {
|
|
54
|
-
* </
|
|
60
|
+
* <Carousel
|
|
61
|
+
* data={largeDataArray}
|
|
62
|
+
* renderItem={(item, index) => <View><Text>{index}: {item}</Text></View>}
|
|
63
|
+
* />
|
|
55
64
|
* ```
|
|
56
65
|
*
|
|
57
66
|
* @param props - 组件属性
|
|
58
67
|
* @returns 轮播图组件
|
|
59
68
|
*/
|
|
60
|
-
export const Carousel = (props: CarouselProps) => {
|
|
69
|
+
export const Carousel = <Data = unknown,>(props: CarouselProps<Data>) => {
|
|
61
70
|
const {
|
|
62
71
|
defaultIndex = 0,
|
|
63
72
|
allowTouchMove = true,
|
|
@@ -73,8 +82,8 @@ export const Carousel = (props: CarouselProps) => {
|
|
|
73
82
|
stuckAtBoundary = true,
|
|
74
83
|
rubberband = true,
|
|
75
84
|
virtualOverscan = 2,
|
|
76
|
-
|
|
77
|
-
|
|
85
|
+
data,
|
|
86
|
+
renderItem,
|
|
78
87
|
style,
|
|
79
88
|
blockNativeResponder = true,
|
|
80
89
|
ref,
|
|
@@ -83,27 +92,31 @@ export const Carousel = (props: CarouselProps) => {
|
|
|
83
92
|
const styles = useCarouselStyles();
|
|
84
93
|
const theme = useTheme();
|
|
85
94
|
|
|
95
|
+
// 使用 state 存储 layout,使依赖更明确
|
|
86
96
|
const [layout, setLayout] = useState<LayoutState>({ width: 0, height: 0 });
|
|
87
|
-
const [dragging, setDragging] = useState(false);
|
|
88
|
-
const [animating, setAnimating] = useState(false);
|
|
89
97
|
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
}, [slideSize]);
|
|
98
|
+
// 分离拖动和动画状态,避免状态冲突
|
|
99
|
+
const [isDragging, setIsDragging] = useState(false);
|
|
100
|
+
const [isAnimating, setIsAnimating] = useState(false);
|
|
94
101
|
|
|
95
|
-
|
|
102
|
+
// 合并归一化计算,减少 useMemo 调用
|
|
103
|
+
const { normalizedSlideSize, normalizedTrackOffset } = useMemo(() => {
|
|
104
|
+
const slideSize_ = Number.isFinite(slideSize) ? clamp(slideSize, 0, 100) : 100;
|
|
105
|
+
let trackOffset_: number;
|
|
96
106
|
if (stuckAtBoundary) {
|
|
97
|
-
|
|
107
|
+
trackOffset_ = 100 - slideSize_;
|
|
108
|
+
} else {
|
|
109
|
+
trackOffset_ = Number.isFinite(trackOffset)
|
|
110
|
+
? clamp(trackOffset, 0, 100 - slideSize_)
|
|
111
|
+
: 0;
|
|
98
112
|
}
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
113
|
+
return {
|
|
114
|
+
normalizedSlideSize: slideSize_,
|
|
115
|
+
normalizedTrackOffset: trackOffset_,
|
|
116
|
+
};
|
|
117
|
+
}, [slideSize, trackOffset, stuckAtBoundary]);
|
|
102
118
|
|
|
103
|
-
const total =
|
|
104
|
-
const normalizedDefault = getDefaultTotal(children);
|
|
105
|
-
return totalProp ?? normalizedDefault;
|
|
106
|
-
}, [children, totalProp]);
|
|
119
|
+
const total = data.length;
|
|
107
120
|
|
|
108
121
|
const loopEnabled = loop && total > 1;
|
|
109
122
|
|
|
@@ -129,17 +142,15 @@ export const Carousel = (props: CarouselProps) => {
|
|
|
129
142
|
|
|
130
143
|
const extTotal = loopEnabled ? total + clonesBefore + clonesAfter : total;
|
|
131
144
|
|
|
132
|
-
|
|
145
|
+
// 合并像素计算,减少重复的 trackSize 获取
|
|
146
|
+
const { slidePixels, trackOffsetPixels } = useMemo(() => {
|
|
133
147
|
const trackSize = direction === 'horizontal' ? layout.width : layout.height;
|
|
134
|
-
if (trackSize <= 0) return 0;
|
|
135
|
-
return
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
if (trackSize <= 0) return 0;
|
|
141
|
-
return trackSize * (normalizedTrackOffset / 100);
|
|
142
|
-
}, [direction, layout.height, layout.width, normalizedTrackOffset]);
|
|
148
|
+
if (trackSize <= 0) return { slidePixels: 0, trackOffsetPixels: 0 };
|
|
149
|
+
return {
|
|
150
|
+
slidePixels: trackSize * (normalizedSlideSize / 100),
|
|
151
|
+
trackOffsetPixels: trackSize * (normalizedTrackOffset / 100),
|
|
152
|
+
};
|
|
153
|
+
}, [direction, layout, normalizedSlideSize, normalizedTrackOffset]);
|
|
143
154
|
|
|
144
155
|
const {
|
|
145
156
|
current,
|
|
@@ -167,6 +178,15 @@ export const Carousel = (props: CarouselProps) => {
|
|
|
167
178
|
const { position, positionValueRef, animateTo, setPositionImmediate } =
|
|
168
179
|
useCarouselPosition();
|
|
169
180
|
|
|
181
|
+
// 状态设置函数
|
|
182
|
+
const setDragging = useCallback((dragging: boolean) => {
|
|
183
|
+
setIsDragging(dragging);
|
|
184
|
+
}, []);
|
|
185
|
+
|
|
186
|
+
const setAnimating = useCallback((animating: boolean) => {
|
|
187
|
+
setIsAnimating(animating);
|
|
188
|
+
}, []);
|
|
189
|
+
|
|
170
190
|
// 切换逻辑
|
|
171
191
|
const { swipeNext, swipePrev, swipeToExtIndex } = useCarouselSwipe({
|
|
172
192
|
total,
|
|
@@ -191,7 +211,7 @@ export const Carousel = (props: CarouselProps) => {
|
|
|
191
211
|
autoplay,
|
|
192
212
|
autoplayInterval,
|
|
193
213
|
total,
|
|
194
|
-
dragging,
|
|
214
|
+
dragging: isDragging,
|
|
195
215
|
current,
|
|
196
216
|
swipeNext,
|
|
197
217
|
swipePrev,
|
|
@@ -213,8 +233,10 @@ export const Carousel = (props: CarouselProps) => {
|
|
|
213
233
|
const onLayoutTrack = useCallback((e: LayoutChangeEvent) => {
|
|
214
234
|
const { width, height } = e.nativeEvent.layout;
|
|
215
235
|
setLayout(prev => {
|
|
216
|
-
if (prev.width
|
|
217
|
-
|
|
236
|
+
if (prev.width !== width || prev.height !== height) {
|
|
237
|
+
return { width, height };
|
|
238
|
+
}
|
|
239
|
+
return prev;
|
|
218
240
|
});
|
|
219
241
|
}, []);
|
|
220
242
|
|
|
@@ -248,20 +270,20 @@ export const Carousel = (props: CarouselProps) => {
|
|
|
248
270
|
}, [direction, position, trackOffsetPixels]);
|
|
249
271
|
|
|
250
272
|
// 计算虚拟渲染的范围
|
|
251
|
-
//
|
|
252
|
-
// 通过 dragging 和 animating 状态的变化来触发虚拟范围的更新
|
|
273
|
+
// 使用 current 状态而不是 extIndexRef,确保虚拟范围与当前索引同步
|
|
253
274
|
const virtualRange = useMemo(() => {
|
|
254
275
|
if (total <= 0 || slidePixels <= 0) {
|
|
255
276
|
return null;
|
|
256
277
|
}
|
|
257
278
|
|
|
258
|
-
const isVirtual =
|
|
259
|
-
|
|
279
|
+
const isVirtual = total > VIRTUAL_RENDER_THRESHOLD;
|
|
280
|
+
const isInteracting = isDragging || isAnimating;
|
|
260
281
|
const effectiveOverscan = isVirtual
|
|
261
|
-
? Math.max(virtualOverscan,
|
|
282
|
+
? Math.max(virtualOverscan, isInteracting ? 1 : 0)
|
|
262
283
|
: virtualOverscan;
|
|
263
284
|
|
|
264
|
-
|
|
285
|
+
// 使用 current 计算 extIndex,确保响应式更新
|
|
286
|
+
const currentExtIndex = loopEnabled ? current + clonesBefore : current;
|
|
265
287
|
const start = isVirtual
|
|
266
288
|
? clamp(currentExtIndex - effectiveOverscan, 0, extTotal - 1)
|
|
267
289
|
: 0;
|
|
@@ -273,88 +295,56 @@ export const Carousel = (props: CarouselProps) => {
|
|
|
273
295
|
}, [
|
|
274
296
|
total,
|
|
275
297
|
slidePixels,
|
|
276
|
-
children,
|
|
277
|
-
totalProp,
|
|
278
298
|
virtualOverscan,
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
extTotal,
|
|
283
|
-
]);
|
|
284
|
-
|
|
285
|
-
// 计算滑块包装器样式
|
|
286
|
-
const slideWrapperStyle: ViewStyle = useMemo(() => {
|
|
287
|
-
return direction === 'horizontal'
|
|
288
|
-
? { width: slidePixels, height: '100%' }
|
|
289
|
-
: { height: slidePixels, width: '100%' };
|
|
290
|
-
}, [direction, slidePixels]);
|
|
291
|
-
|
|
292
|
-
// 计算虚拟渲染的占位空间样式
|
|
293
|
-
const spacerStyles = useMemo(() => {
|
|
294
|
-
if (!virtualRange) return null;
|
|
295
|
-
|
|
296
|
-
const { start, end } = virtualRange;
|
|
297
|
-
const leadingSize = start * slidePixels;
|
|
298
|
-
const trailingSize = (extTotal - end - 1) * slidePixels;
|
|
299
|
-
|
|
300
|
-
return {
|
|
301
|
-
leading:
|
|
302
|
-
direction === 'horizontal'
|
|
303
|
-
? { width: leadingSize }
|
|
304
|
-
: { height: leadingSize },
|
|
305
|
-
trailing:
|
|
306
|
-
direction === 'horizontal'
|
|
307
|
-
? { width: trailingSize }
|
|
308
|
-
: { height: trailingSize },
|
|
309
|
-
leadingSize,
|
|
310
|
-
trailingSize,
|
|
311
|
-
};
|
|
312
|
-
}, [virtualRange, slidePixels, extTotal, direction]);
|
|
313
|
-
|
|
314
|
-
const { renderSlides } = useCarouselSlides({
|
|
315
|
-
children,
|
|
316
|
-
total,
|
|
317
|
-
virtualRange,
|
|
299
|
+
isDragging,
|
|
300
|
+
isAnimating,
|
|
301
|
+
current,
|
|
318
302
|
loopEnabled,
|
|
319
303
|
clonesBefore,
|
|
320
|
-
|
|
321
|
-
|
|
322
|
-
slideStyles: styles,
|
|
323
|
-
});
|
|
304
|
+
extTotal,
|
|
305
|
+
]);
|
|
324
306
|
|
|
325
|
-
const
|
|
326
|
-
|
|
327
|
-
|
|
328
|
-
|
|
329
|
-
current,
|
|
330
|
-
activeColor: theme.palette.brand7,
|
|
331
|
-
inactiveColor: theme.palette.fontGray5,
|
|
332
|
-
styles,
|
|
333
|
-
});
|
|
307
|
+
const trackInnerStyle =
|
|
308
|
+
direction === 'horizontal'
|
|
309
|
+
? styles.trackInnerHorizontal
|
|
310
|
+
: styles.trackInnerVertical;
|
|
334
311
|
|
|
335
|
-
|
|
336
|
-
|
|
337
|
-
|
|
338
|
-
|
|
339
|
-
}, [direction]);
|
|
312
|
+
// 边界情况处理:空数据
|
|
313
|
+
if (!data || data.length === 0) {
|
|
314
|
+
return null;
|
|
315
|
+
}
|
|
340
316
|
|
|
341
317
|
return (
|
|
342
|
-
<View style={
|
|
318
|
+
<View style={[styles.container, style]}>
|
|
343
319
|
<View
|
|
344
320
|
style={styles.track}
|
|
345
321
|
onLayout={onLayoutTrack}
|
|
346
322
|
{...panResponder?.panHandlers}
|
|
347
323
|
>
|
|
348
324
|
<Animated.View
|
|
349
|
-
style={
|
|
350
|
-
styles.trackInner,
|
|
351
|
-
trackInnerStyle,
|
|
352
|
-
trackTransformStyle,
|
|
353
|
-
])}
|
|
325
|
+
style={[styles.trackInner, trackInnerStyle, trackTransformStyle]}
|
|
354
326
|
>
|
|
355
|
-
|
|
327
|
+
<CarouselSlides<Data>
|
|
328
|
+
data={data}
|
|
329
|
+
renderItem={renderItem}
|
|
330
|
+
total={total}
|
|
331
|
+
virtualRange={virtualRange}
|
|
332
|
+
loopEnabled={loopEnabled}
|
|
333
|
+
clonesBefore={clonesBefore}
|
|
334
|
+
direction={direction}
|
|
335
|
+
slidePixels={slidePixels}
|
|
336
|
+
extTotal={extTotal}
|
|
337
|
+
/>
|
|
356
338
|
</Animated.View>
|
|
357
|
-
|
|
339
|
+
<CarouselIndicator
|
|
340
|
+
indicator={indicator}
|
|
341
|
+
indicatorProps={indicatorProps}
|
|
342
|
+
total={total}
|
|
343
|
+
current={current}
|
|
344
|
+
activeColor={theme.palette.brand7}
|
|
345
|
+
inactiveColor={theme.palette.fontGray5}
|
|
346
|
+
styles={styles}
|
|
347
|
+
/>
|
|
358
348
|
</View>
|
|
359
349
|
</View>
|
|
360
350
|
);
|
|
@@ -1,8 +1,6 @@
|
|
|
1
1
|
export { useCarouselAutoplay } from './use-carousel-autoplay';
|
|
2
2
|
export { useCarouselIndex } from './use-carousel-index';
|
|
3
|
-
export { useCarouselIndicator } from './use-carousel-indicator';
|
|
4
3
|
export { useCarouselLifecycle } from './use-carousel-lifecycle';
|
|
5
4
|
export { useCarouselPanResponder } from './use-carousel-pan-responder';
|
|
6
5
|
export { useCarouselPosition } from './use-carousel-position';
|
|
7
|
-
export { useCarouselSlides } from './use-carousel-slides';
|
|
8
6
|
export { useCarouselSwipe } from './use-carousel-swipe';
|
|
@@ -2,9 +2,10 @@ import { useMemoizedFn } from 'ahooks';
|
|
|
2
2
|
import { clamp } from 'lodash-es';
|
|
3
3
|
import { useCallback, useRef, useState } from 'react';
|
|
4
4
|
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
5
|
+
/**
|
|
6
|
+
* 橡皮筋效果阻尼系数
|
|
7
|
+
*/
|
|
8
|
+
const RUBBERBAND_DAMPING = 0.35;
|
|
8
9
|
|
|
9
10
|
interface UseCarouselIndexParams {
|
|
10
11
|
total: number;
|
|
@@ -126,12 +127,15 @@ export const useCarouselIndex = (params: UseCarouselIndexParams) => {
|
|
|
126
127
|
if (slidePixels <= 0) return nextPosition;
|
|
127
128
|
if (total <= 0) return 0;
|
|
128
129
|
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
130
|
+
// 计算轮播边界索引范围
|
|
131
|
+
let min = 0;
|
|
132
|
+
let max = total - 1;
|
|
133
|
+
if (stuckAtBoundary) {
|
|
134
|
+
const slideRatio = normalizedSlideSize / 100;
|
|
135
|
+
const offsetRatio = normalizedTrackOffset / 100;
|
|
136
|
+
min = 0 + offsetRatio / (slideRatio || 1);
|
|
137
|
+
max = total - 1 - (1 - slideRatio - offsetRatio) / (slideRatio || 1);
|
|
138
|
+
}
|
|
135
139
|
const minPos = min * slidePixels;
|
|
136
140
|
const maxPos = max * slidePixels;
|
|
137
141
|
|
|
@@ -64,6 +64,7 @@ export const useCarouselLifecycle = (params: UseCarouselLifecycleParams) => {
|
|
|
64
64
|
if (slidePixels > 0) {
|
|
65
65
|
setPositionImmediate(getBoundedPosition(extIndex * slidePixels));
|
|
66
66
|
}
|
|
67
|
+
// eslint-disable-next-line react-hooks/exhaustive-deps
|
|
67
68
|
}, [
|
|
68
69
|
defaultIndex,
|
|
69
70
|
getExtIndexFromIndex,
|
|
@@ -72,8 +73,7 @@ export const useCarouselLifecycle = (params: UseCarouselLifecycleParams) => {
|
|
|
72
73
|
slidePixels,
|
|
73
74
|
total,
|
|
74
75
|
updateCurrent,
|
|
75
|
-
|
|
76
|
-
extIndexRef,
|
|
76
|
+
// refs 不需要在依赖数组中,它们的引用永远不变
|
|
77
77
|
]);
|
|
78
78
|
|
|
79
79
|
// 响应 slidePixels 变化
|
|
@@ -81,5 +81,6 @@ export const useCarouselLifecycle = (params: UseCarouselLifecycleParams) => {
|
|
|
81
81
|
if (slidePixels <= 0) return;
|
|
82
82
|
const extIndex = extIndexRef.current;
|
|
83
83
|
setPositionImmediate(getBoundedPosition(extIndex * slidePixels));
|
|
84
|
-
|
|
84
|
+
// eslint-disable-next-line react-hooks/exhaustive-deps
|
|
85
|
+
}, [getBoundedPosition, setPositionImmediate, slidePixels]);
|
|
85
86
|
};
|
|
@@ -1,12 +1,20 @@
|
|
|
1
1
|
import { clamp } from 'lodash-es';
|
|
2
|
-
import { type RefObject, useMemo } from 'react';
|
|
2
|
+
import { type RefObject, useMemo, useRef } from 'react';
|
|
3
3
|
import {
|
|
4
4
|
type Animated,
|
|
5
5
|
PanResponder,
|
|
6
6
|
type PanResponderInstance,
|
|
7
7
|
} from 'react-native';
|
|
8
8
|
|
|
9
|
-
|
|
9
|
+
/**
|
|
10
|
+
* 手势识别最小移动距离(像素)
|
|
11
|
+
*/
|
|
12
|
+
const GESTURE_MIN_DISTANCE = 5;
|
|
13
|
+
|
|
14
|
+
/**
|
|
15
|
+
* 速度投影系数(用于计算惯性滑动距离)
|
|
16
|
+
*/
|
|
17
|
+
const VELOCITY_PROJECTION_FACTOR = 2000;
|
|
10
18
|
|
|
11
19
|
interface UseCarouselPanResponderParams {
|
|
12
20
|
allowTouchMove: boolean;
|
|
@@ -76,6 +84,26 @@ export const useCarouselPanResponder = (
|
|
|
76
84
|
swipeToExtIndex,
|
|
77
85
|
} = params;
|
|
78
86
|
|
|
87
|
+
// 使用 ref 存储回调,减少 PanResponder 重建
|
|
88
|
+
const callbacksRef = useRef({
|
|
89
|
+
setDragging,
|
|
90
|
+
setAnimating,
|
|
91
|
+
updateCurrent,
|
|
92
|
+
getIndexFromExtIndex,
|
|
93
|
+
getBoundedPosition,
|
|
94
|
+
swipeToExtIndex,
|
|
95
|
+
});
|
|
96
|
+
|
|
97
|
+
// 更新回调 ref
|
|
98
|
+
callbacksRef.current = {
|
|
99
|
+
setDragging,
|
|
100
|
+
setAnimating,
|
|
101
|
+
updateCurrent,
|
|
102
|
+
getIndexFromExtIndex,
|
|
103
|
+
getBoundedPosition,
|
|
104
|
+
swipeToExtIndex,
|
|
105
|
+
};
|
|
106
|
+
|
|
79
107
|
return useMemo(() => {
|
|
80
108
|
if (!allowTouchMove) return null;
|
|
81
109
|
if (total <= 1) return null;
|
|
@@ -92,8 +120,8 @@ export const useCarouselPanResponder = (
|
|
|
92
120
|
return PanResponder.create({
|
|
93
121
|
onMoveShouldSetPanResponder: shouldSet,
|
|
94
122
|
onPanResponderGrant: () => {
|
|
95
|
-
setDragging(true);
|
|
96
|
-
setAnimating(false);
|
|
123
|
+
callbacksRef.current.setDragging(true);
|
|
124
|
+
callbacksRef.current.setAnimating(false);
|
|
97
125
|
position.current.stopAnimation();
|
|
98
126
|
startPosition = positionValueRef.current;
|
|
99
127
|
},
|
|
@@ -117,7 +145,7 @@ export const useCarouselPanResponder = (
|
|
|
117
145
|
}
|
|
118
146
|
}
|
|
119
147
|
|
|
120
|
-
const bounded = getBoundedPosition(nextPosition);
|
|
148
|
+
const bounded = callbacksRef.current.getBoundedPosition(nextPosition);
|
|
121
149
|
position.current.setValue(bounded);
|
|
122
150
|
positionValueRef.current = bounded;
|
|
123
151
|
|
|
@@ -127,10 +155,12 @@ export const useCarouselPanResponder = (
|
|
|
127
155
|
extTotal - 1,
|
|
128
156
|
);
|
|
129
157
|
extIndexRef.current = nextExtIndex;
|
|
130
|
-
updateCurrent(
|
|
158
|
+
callbacksRef.current.updateCurrent(
|
|
159
|
+
callbacksRef.current.getIndexFromExtIndex(nextExtIndex),
|
|
160
|
+
);
|
|
131
161
|
},
|
|
132
162
|
onPanResponderRelease: (_evt, gestureState) => {
|
|
133
|
-
setDragging(false);
|
|
163
|
+
callbacksRef.current.setDragging(false);
|
|
134
164
|
const velocity = axis === 'x' ? gestureState.vx : gestureState.vy;
|
|
135
165
|
position.current.stopAnimation(value => {
|
|
136
166
|
const baseIndex = value / slidePixels;
|
|
@@ -141,12 +171,12 @@ export const useCarouselPanResponder = (
|
|
|
141
171
|
const maxAdj = minAdj + 1;
|
|
142
172
|
const rounded = Math.round(projectedIndex);
|
|
143
173
|
const adjacent = clamp(rounded, minAdj, maxAdj);
|
|
144
|
-
swipeToExtIndex(adjacent);
|
|
174
|
+
callbacksRef.current.swipeToExtIndex(adjacent);
|
|
145
175
|
});
|
|
146
176
|
},
|
|
147
177
|
onPanResponderTerminate: () => {
|
|
148
|
-
setDragging(false);
|
|
149
|
-
swipeToExtIndex(extIndexRef.current);
|
|
178
|
+
callbacksRef.current.setDragging(false);
|
|
179
|
+
callbacksRef.current.swipeToExtIndex(extIndexRef.current);
|
|
150
180
|
},
|
|
151
181
|
onPanResponderTerminationRequest: () => false,
|
|
152
182
|
onShouldBlockNativeResponder: () => blockNativeResponder,
|
|
@@ -158,15 +188,9 @@ export const useCarouselPanResponder = (
|
|
|
158
188
|
direction,
|
|
159
189
|
extTotal,
|
|
160
190
|
extIndexRef,
|
|
161
|
-
getBoundedPosition,
|
|
162
|
-
getIndexFromExtIndex,
|
|
163
191
|
position,
|
|
164
192
|
positionValueRef,
|
|
165
|
-
setAnimating,
|
|
166
|
-
setDragging,
|
|
167
193
|
slidePixels,
|
|
168
|
-
swipeToExtIndex,
|
|
169
194
|
total,
|
|
170
|
-
updateCurrent,
|
|
171
195
|
]);
|
|
172
196
|
};
|
|
@@ -1,8 +1,6 @@
|
|
|
1
1
|
import { Carousel as _Carousel } from './carousel';
|
|
2
|
-
import { CarouselItem } from './carousel-item';
|
|
3
2
|
|
|
4
3
|
export * from './carousel';
|
|
5
|
-
export * from './carousel-item';
|
|
6
4
|
export type * from './types';
|
|
7
5
|
|
|
8
|
-
export const Carousel =
|
|
6
|
+
export const Carousel = _Carousel;
|
|
@@ -16,6 +16,18 @@ export const useCarouselStyles = createThemedStyles(theme => ({
|
|
|
16
16
|
flexGrow: 0,
|
|
17
17
|
flexShrink: 0,
|
|
18
18
|
},
|
|
19
|
+
trackInnerHorizontal: {
|
|
20
|
+
flexDirection: 'row',
|
|
21
|
+
height: '100%',
|
|
22
|
+
flexGrow: 0,
|
|
23
|
+
flexShrink: 0,
|
|
24
|
+
},
|
|
25
|
+
trackInnerVertical: {
|
|
26
|
+
flexDirection: 'column',
|
|
27
|
+
width: '100%',
|
|
28
|
+
flexGrow: 0,
|
|
29
|
+
flexShrink: 0,
|
|
30
|
+
},
|
|
19
31
|
slide: {
|
|
20
32
|
flexGrow: 0,
|
|
21
33
|
flexShrink: 0,
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import type {
|
|
1
|
+
import type { ReactNode, Ref } from 'react';
|
|
2
2
|
import type { StyleProp, ViewStyle } from 'react-native';
|
|
3
3
|
|
|
4
4
|
/** Carousel 组件暴露的实例方法 */
|
|
@@ -12,7 +12,7 @@ export interface CarouselRef {
|
|
|
12
12
|
}
|
|
13
13
|
|
|
14
14
|
/** Carousel 组件属性 */
|
|
15
|
-
export interface CarouselProps {
|
|
15
|
+
export interface CarouselProps<Data = unknown> {
|
|
16
16
|
/**
|
|
17
17
|
* 初始展示的索引
|
|
18
18
|
* 该属性是响应式的,当值变化时轮播会跳转到对应索引
|
|
@@ -101,26 +101,21 @@ export interface CarouselProps {
|
|
|
101
101
|
|
|
102
102
|
/**
|
|
103
103
|
* 虚拟渲染时前后额外渲染的滑块个数
|
|
104
|
-
* 仅在使用 render prop 且传入 total 时生效
|
|
105
104
|
* @default 2
|
|
106
105
|
*/
|
|
107
106
|
virtualOverscan?: number;
|
|
108
107
|
|
|
109
108
|
/**
|
|
110
|
-
*
|
|
111
|
-
* 使用 render prop children 时需要手动传入
|
|
109
|
+
* 数据源数组
|
|
112
110
|
*/
|
|
113
|
-
|
|
111
|
+
data: Data[];
|
|
114
112
|
|
|
115
113
|
/**
|
|
116
|
-
*
|
|
117
|
-
*
|
|
118
|
-
*
|
|
114
|
+
* 渲染每一项的函数
|
|
115
|
+
* @param item - 数据项
|
|
116
|
+
* @param index - 索引
|
|
119
117
|
*/
|
|
120
|
-
|
|
121
|
-
| ReactElement<CarouselItemProps>
|
|
122
|
-
| ReactElement<CarouselItemProps>[]
|
|
123
|
-
| ((index: number) => ReactElement<CarouselItemProps>);
|
|
118
|
+
renderItem: (item: Data, index: number) => ReactNode;
|
|
124
119
|
|
|
125
120
|
/**
|
|
126
121
|
* 是否阻断原生手势响应
|
|
@@ -134,11 +129,3 @@ export interface CarouselProps {
|
|
|
134
129
|
|
|
135
130
|
ref?: Ref<CarouselRef>;
|
|
136
131
|
}
|
|
137
|
-
|
|
138
|
-
/** CarouselItem 组件属性 */
|
|
139
|
-
export interface CarouselItemProps {
|
|
140
|
-
/** 滑块内容 */
|
|
141
|
-
children?: ReactNode;
|
|
142
|
-
/** 滑块容器样式 */
|
|
143
|
-
style?: StyleProp<ViewStyle>;
|
|
144
|
-
}
|