react-native-reanimated-carousel 2.0.0 → 2.2.1
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/README.md +40 -10
- package/README.zh-CN.md +42 -9
- package/lib/commonjs/Carousel.js +20 -10
- package/lib/commonjs/Carousel.js.map +1 -1
- package/lib/commonjs/ScrollViewGesture.js +13 -5
- package/lib/commonjs/ScrollViewGesture.js.map +1 -1
- package/lib/commonjs/constants/index.js +9 -1
- package/lib/commonjs/constants/index.js.map +1 -1
- package/lib/commonjs/hooks/useAutoPlay.js +22 -16
- package/lib/commonjs/hooks/useAutoPlay.js.map +1 -1
- package/lib/commonjs/hooks/useCarouselController.js +94 -30
- package/lib/commonjs/hooks/useCarouselController.js.map +1 -1
- package/lib/commonjs/hooks/useInitProps.js +4 -1
- package/lib/commonjs/hooks/useInitProps.js.map +1 -1
- package/lib/commonjs/layouts/BaseLayout.js +14 -8
- package/lib/commonjs/layouts/BaseLayout.js.map +1 -1
- package/lib/commonjs/layouts/normal.js +1 -1
- package/lib/commonjs/layouts/normal.js.map +1 -1
- package/lib/commonjs/layouts/parallax.js.map +1 -1
- package/lib/commonjs/layouts/stack.js +15 -0
- package/lib/commonjs/layouts/stack.js.map +1 -1
- package/lib/module/Carousel.js +20 -10
- package/lib/module/Carousel.js.map +1 -1
- package/lib/module/ScrollViewGesture.js +13 -6
- package/lib/module/ScrollViewGesture.js.map +1 -1
- package/lib/module/constants/index.js +5 -0
- package/lib/module/constants/index.js.map +1 -1
- package/lib/module/hooks/useAutoPlay.js +22 -16
- package/lib/module/hooks/useAutoPlay.js.map +1 -1
- package/lib/module/hooks/useCarouselController.js +91 -30
- package/lib/module/hooks/useCarouselController.js.map +1 -1
- package/lib/module/hooks/useInitProps.js +4 -1
- package/lib/module/hooks/useInitProps.js.map +1 -1
- package/lib/module/layouts/BaseLayout.js +14 -10
- package/lib/module/layouts/BaseLayout.js.map +1 -1
- package/lib/module/layouts/normal.js +2 -2
- package/lib/module/layouts/normal.js.map +1 -1
- package/lib/module/layouts/parallax.js.map +1 -1
- package/lib/module/layouts/stack.js +12 -0
- package/lib/module/layouts/stack.js.map +1 -1
- package/lib/typescript/constants/index.d.ts +3 -0
- package/lib/typescript/hooks/useAutoPlay.d.ts +1 -1
- package/lib/typescript/hooks/useCarouselController.d.ts +8 -5
- package/lib/typescript/hooks/useInitProps.d.ts +3 -6
- package/lib/typescript/layouts/BaseLayout.d.ts +3 -0
- package/lib/typescript/layouts/parallax.d.ts +9 -1
- package/lib/typescript/layouts/stack.d.ts +25 -0
- package/lib/typescript/types.d.ts +45 -32
- package/package.json +11 -4
- package/src/Carousel.tsx +27 -8
- package/src/ScrollViewGesture.tsx +12 -5
- package/src/constants/index.ts +6 -0
- package/src/hooks/useAutoPlay.ts +20 -23
- package/src/hooks/useCarouselController.tsx +112 -50
- package/src/hooks/useInitProps.ts +18 -8
- package/src/layouts/BaseLayout.tsx +26 -12
- package/src/layouts/normal.ts +2 -7
- package/src/layouts/parallax.ts +10 -1
- package/src/layouts/stack.ts +42 -0
- package/src/types.ts +50 -32
- package/CHANGELOG.md +0 -324
- package/lib/commonjs/hooks/useIndexController.js +0 -65
- package/lib/commonjs/hooks/useIndexController.js.map +0 -1
- package/lib/module/hooks/useIndexController.js +0 -52
- package/lib/module/hooks/useIndexController.js.map +0 -1
- package/lib/typescript/hooks/useIndexController.d.ts +0 -18
- package/src/.DS_Store +0 -0
- package/src/hooks/useIndexController.ts +0 -78
|
@@ -12,8 +12,9 @@ import Animated, {
|
|
|
12
12
|
useDerivedValue,
|
|
13
13
|
useSharedValue,
|
|
14
14
|
withDecay,
|
|
15
|
-
|
|
15
|
+
withTiming,
|
|
16
16
|
} from 'react-native-reanimated';
|
|
17
|
+
import { Easing } from './constants';
|
|
17
18
|
import { CTX } from './store';
|
|
18
19
|
|
|
19
20
|
type GestureContext = {
|
|
@@ -40,6 +41,7 @@ const IScrollViewGesture: React.FC<Props> = (props) => {
|
|
|
40
41
|
enableSnap,
|
|
41
42
|
panGestureHandlerProps,
|
|
42
43
|
loop: infinite,
|
|
44
|
+
scrollAnimationDuration,
|
|
43
45
|
},
|
|
44
46
|
} = React.useContext(CTX);
|
|
45
47
|
|
|
@@ -54,10 +56,11 @@ const IScrollViewGesture: React.FC<Props> = (props) => {
|
|
|
54
56
|
const _withSpring = React.useCallback(
|
|
55
57
|
(toValue: number, onFinished?: () => void) => {
|
|
56
58
|
'worklet';
|
|
57
|
-
return
|
|
59
|
+
return withTiming(
|
|
58
60
|
toValue,
|
|
59
61
|
{
|
|
60
|
-
|
|
62
|
+
duration: scrollAnimationDuration,
|
|
63
|
+
easing: Easing.easeOutQuart,
|
|
61
64
|
},
|
|
62
65
|
(isFinished) => {
|
|
63
66
|
if (isFinished) {
|
|
@@ -66,7 +69,7 @@ const IScrollViewGesture: React.FC<Props> = (props) => {
|
|
|
66
69
|
}
|
|
67
70
|
);
|
|
68
71
|
},
|
|
69
|
-
[]
|
|
72
|
+
[scrollAnimationDuration]
|
|
70
73
|
);
|
|
71
74
|
|
|
72
75
|
const endWithSpring = React.useCallback(
|
|
@@ -199,7 +202,11 @@ const IScrollViewGesture: React.FC<Props> = (props) => {
|
|
|
199
202
|
!infinite &&
|
|
200
203
|
(translation.value > 0 || translation.value < -ctx.max)
|
|
201
204
|
) {
|
|
202
|
-
|
|
205
|
+
const boundary = translation.value > 0 ? 0 : -ctx.max;
|
|
206
|
+
const fixed = boundary - ctx.panOffset;
|
|
207
|
+
const dynamic = panTranslation - fixed;
|
|
208
|
+
translation.value = boundary + dynamic * 0.5;
|
|
209
|
+
return;
|
|
203
210
|
}
|
|
204
211
|
|
|
205
212
|
translation.value = ctx.panOffset + panTranslation;
|
package/src/constants/index.ts
CHANGED
package/src/hooks/useAutoPlay.ts
CHANGED
|
@@ -15,44 +15,41 @@ export function useAutoPlay(opts: {
|
|
|
15
15
|
} = opts;
|
|
16
16
|
|
|
17
17
|
const timer = React.useRef<NodeJS.Timer>();
|
|
18
|
+
const stopped = React.useRef<boolean>(!autoPlay);
|
|
18
19
|
|
|
19
|
-
const
|
|
20
|
-
|
|
21
|
-
}, []);
|
|
22
|
-
|
|
23
|
-
const run = React.useCallback(() => {
|
|
24
|
-
if (timer.current) {
|
|
25
|
-
pause();
|
|
26
|
-
}
|
|
27
|
-
|
|
28
|
-
if (!autoPlay) {
|
|
20
|
+
const play = React.useCallback(() => {
|
|
21
|
+
if (stopped.current) {
|
|
29
22
|
return;
|
|
30
23
|
}
|
|
31
24
|
|
|
32
|
-
timer.current =
|
|
25
|
+
timer.current = setTimeout(() => {
|
|
33
26
|
autoPlayReverse
|
|
34
|
-
? carouselController.prev()
|
|
35
|
-
: carouselController.next();
|
|
27
|
+
? carouselController.prev({ onFinished: play })
|
|
28
|
+
: carouselController.next({ onFinished: play });
|
|
36
29
|
}, autoPlayInterval);
|
|
37
|
-
}, [
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
30
|
+
}, [autoPlayReverse, autoPlayInterval, carouselController]);
|
|
31
|
+
|
|
32
|
+
const pause = React.useCallback(() => {
|
|
33
|
+
timer.current && clearInterval(timer.current);
|
|
34
|
+
stopped.current = true;
|
|
35
|
+
}, []);
|
|
36
|
+
|
|
37
|
+
const start = React.useCallback(() => {
|
|
38
|
+
stopped.current = false;
|
|
39
|
+
play();
|
|
40
|
+
}, [play]);
|
|
44
41
|
|
|
45
42
|
React.useEffect(() => {
|
|
46
43
|
if (autoPlay) {
|
|
47
|
-
|
|
44
|
+
start();
|
|
48
45
|
} else {
|
|
49
46
|
pause();
|
|
50
47
|
}
|
|
51
48
|
return pause;
|
|
52
|
-
}, [
|
|
49
|
+
}, [pause, start, autoPlay]);
|
|
53
50
|
|
|
54
51
|
return {
|
|
55
|
-
run,
|
|
56
52
|
pause,
|
|
53
|
+
start,
|
|
57
54
|
};
|
|
58
55
|
}
|
|
@@ -1,17 +1,20 @@
|
|
|
1
1
|
import React from 'react';
|
|
2
2
|
import type Animated from 'react-native-reanimated';
|
|
3
|
-
import {
|
|
3
|
+
import { Easing } from '../constants';
|
|
4
|
+
import { runOnJS, useSharedValue, withTiming } from 'react-native-reanimated';
|
|
5
|
+
import type { TCarouselActionOptions } from '../types';
|
|
4
6
|
|
|
5
7
|
interface IOpts {
|
|
6
8
|
loop: boolean;
|
|
7
9
|
size: number;
|
|
8
10
|
handlerOffsetX: Animated.SharedValue<number>;
|
|
9
11
|
disable?: boolean;
|
|
12
|
+
duration?: number;
|
|
13
|
+
originalLength: number;
|
|
14
|
+
length: number;
|
|
10
15
|
onScrollBegin?: () => void;
|
|
11
16
|
onScrollEnd?: () => void;
|
|
12
17
|
// the length before fill data
|
|
13
|
-
originalLength: number;
|
|
14
|
-
length: number;
|
|
15
18
|
onChange: (index: number) => void;
|
|
16
19
|
}
|
|
17
20
|
|
|
@@ -20,14 +23,15 @@ export interface ICarouselController {
|
|
|
20
23
|
index: Animated.SharedValue<number>;
|
|
21
24
|
sharedIndex: React.MutableRefObject<number>;
|
|
22
25
|
sharedPreIndex: React.MutableRefObject<number>;
|
|
23
|
-
prev: () => void;
|
|
24
|
-
next: () => void;
|
|
26
|
+
prev: (opts?: TCarouselActionOptions) => void;
|
|
27
|
+
next: (opts?: TCarouselActionOptions) => void;
|
|
25
28
|
computedIndex: () => void;
|
|
26
29
|
getCurrentIndex: () => number;
|
|
27
30
|
to: (index: number, animated?: boolean) => void;
|
|
31
|
+
scrollTo: (opts?: TCarouselActionOptions) => void;
|
|
28
32
|
}
|
|
29
33
|
|
|
30
|
-
export function useCarouselController(
|
|
34
|
+
export function useCarouselController(options: IOpts): ICarouselController {
|
|
31
35
|
const {
|
|
32
36
|
size,
|
|
33
37
|
loop,
|
|
@@ -36,13 +40,27 @@ export function useCarouselController(opts: IOpts): ICarouselController {
|
|
|
36
40
|
originalLength,
|
|
37
41
|
length,
|
|
38
42
|
onChange,
|
|
39
|
-
|
|
43
|
+
duration,
|
|
44
|
+
} = options;
|
|
40
45
|
|
|
41
46
|
const index = useSharedValue<number>(0);
|
|
42
47
|
// The Index displayed to the user
|
|
43
48
|
const sharedIndex = React.useRef<number>(0);
|
|
44
49
|
const sharedPreIndex = React.useRef<number>(0);
|
|
45
50
|
|
|
51
|
+
const currentFixedPage = React.useCallback(() => {
|
|
52
|
+
if (loop) {
|
|
53
|
+
return -Math.round(handlerOffsetX.value / size);
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
const fixed = (handlerOffsetX.value / size) % length;
|
|
57
|
+
return Math.round(
|
|
58
|
+
handlerOffsetX.value <= 0
|
|
59
|
+
? Math.abs(fixed)
|
|
60
|
+
: Math.abs(fixed > 0 ? length - fixed : 0)
|
|
61
|
+
);
|
|
62
|
+
}, [handlerOffsetX, length, size, loop]);
|
|
63
|
+
|
|
46
64
|
const convertToSharedIndex = React.useCallback(
|
|
47
65
|
(i: number) => {
|
|
48
66
|
if (loop) {
|
|
@@ -89,65 +107,93 @@ export function useCarouselController(opts: IOpts): ICarouselController {
|
|
|
89
107
|
}, [disable]);
|
|
90
108
|
|
|
91
109
|
const onScrollEnd = React.useCallback(() => {
|
|
92
|
-
|
|
93
|
-
}, [
|
|
110
|
+
options.onScrollEnd?.();
|
|
111
|
+
}, [options]);
|
|
94
112
|
|
|
95
113
|
const onScrollBegin = React.useCallback(() => {
|
|
96
|
-
|
|
97
|
-
}, [
|
|
114
|
+
options.onScrollBegin?.();
|
|
115
|
+
}, [options]);
|
|
98
116
|
|
|
99
|
-
const
|
|
100
|
-
(toValue: number,
|
|
101
|
-
return
|
|
117
|
+
const scrollWithTiming = React.useCallback(
|
|
118
|
+
(toValue: number, onFinished?: () => void) => {
|
|
119
|
+
return withTiming(
|
|
102
120
|
toValue,
|
|
103
|
-
{
|
|
121
|
+
{ duration, easing: Easing.easeOutQuart },
|
|
104
122
|
(isFinished: boolean) => {
|
|
105
|
-
callback?.();
|
|
106
123
|
if (isFinished) {
|
|
107
124
|
runOnJS(onScrollEnd)();
|
|
125
|
+
onFinished && runOnJS(onFinished)();
|
|
108
126
|
}
|
|
109
127
|
}
|
|
110
128
|
);
|
|
111
129
|
},
|
|
112
|
-
[onScrollEnd]
|
|
130
|
+
[onScrollEnd, duration]
|
|
113
131
|
);
|
|
114
132
|
|
|
115
|
-
const next = React.useCallback(
|
|
116
|
-
|
|
133
|
+
const next = React.useCallback(
|
|
134
|
+
(opts: TCarouselActionOptions = {}) => {
|
|
135
|
+
const { count = 1, animated = true, onFinished } = opts;
|
|
136
|
+
if (!canSliding() || (!loop && index.value >= length - 1)) return;
|
|
117
137
|
|
|
118
|
-
|
|
138
|
+
onScrollBegin?.();
|
|
119
139
|
|
|
120
|
-
|
|
140
|
+
const nextPage = currentFixedPage() + count;
|
|
141
|
+
index.value = nextPage;
|
|
121
142
|
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
143
|
+
if (animated) {
|
|
144
|
+
handlerOffsetX.value = scrollWithTiming(
|
|
145
|
+
-nextPage * size,
|
|
146
|
+
onFinished
|
|
147
|
+
);
|
|
148
|
+
} else {
|
|
149
|
+
handlerOffsetX.value = -nextPage * size;
|
|
150
|
+
onFinished?.();
|
|
151
|
+
}
|
|
152
|
+
},
|
|
153
|
+
[
|
|
154
|
+
canSliding,
|
|
155
|
+
loop,
|
|
156
|
+
index,
|
|
157
|
+
length,
|
|
158
|
+
onScrollBegin,
|
|
159
|
+
handlerOffsetX,
|
|
160
|
+
size,
|
|
161
|
+
scrollWithTiming,
|
|
162
|
+
currentFixedPage,
|
|
163
|
+
]
|
|
164
|
+
);
|
|
133
165
|
|
|
134
|
-
const prev = React.useCallback(
|
|
135
|
-
|
|
166
|
+
const prev = React.useCallback(
|
|
167
|
+
(opts: TCarouselActionOptions = {}) => {
|
|
168
|
+
const { count = 1, animated = true, onFinished } = opts;
|
|
169
|
+
if (!canSliding() || (!loop && index.value <= 0)) return;
|
|
136
170
|
|
|
137
|
-
|
|
171
|
+
onScrollBegin?.();
|
|
138
172
|
|
|
139
|
-
|
|
173
|
+
const prevPage = currentFixedPage() - count;
|
|
174
|
+
index.value = prevPage;
|
|
140
175
|
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
176
|
+
if (animated) {
|
|
177
|
+
handlerOffsetX.value = scrollWithTiming(
|
|
178
|
+
-prevPage * size,
|
|
179
|
+
onFinished
|
|
180
|
+
);
|
|
181
|
+
} else {
|
|
182
|
+
handlerOffsetX.value = -prevPage * size;
|
|
183
|
+
onFinished?.();
|
|
184
|
+
}
|
|
185
|
+
},
|
|
186
|
+
[
|
|
187
|
+
canSliding,
|
|
188
|
+
loop,
|
|
189
|
+
index,
|
|
190
|
+
onScrollBegin,
|
|
191
|
+
handlerOffsetX,
|
|
192
|
+
size,
|
|
193
|
+
scrollWithTiming,
|
|
194
|
+
currentFixedPage,
|
|
195
|
+
]
|
|
196
|
+
);
|
|
151
197
|
|
|
152
198
|
const to = React.useCallback(
|
|
153
199
|
(idx: number, animated: boolean = false) => {
|
|
@@ -159,9 +205,8 @@ export function useCarouselController(opts: IOpts): ICarouselController {
|
|
|
159
205
|
const offset = handlerOffsetX.value + (index.value - idx) * size;
|
|
160
206
|
|
|
161
207
|
if (animated) {
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
});
|
|
208
|
+
index.value = idx;
|
|
209
|
+
handlerOffsetX.value = scrollWithTiming(offset);
|
|
165
210
|
} else {
|
|
166
211
|
handlerOffsetX.value = offset;
|
|
167
212
|
index.value = idx;
|
|
@@ -174,15 +219,32 @@ export function useCarouselController(opts: IOpts): ICarouselController {
|
|
|
174
219
|
onScrollBegin,
|
|
175
220
|
handlerOffsetX,
|
|
176
221
|
size,
|
|
177
|
-
|
|
222
|
+
scrollWithTiming,
|
|
178
223
|
onScrollEnd,
|
|
179
224
|
]
|
|
180
225
|
);
|
|
181
226
|
|
|
227
|
+
const scrollTo = React.useCallback(
|
|
228
|
+
(opts: TCarouselActionOptions = {}) => {
|
|
229
|
+
const { count, animated = false, onFinished } = opts;
|
|
230
|
+
if (!count) {
|
|
231
|
+
return;
|
|
232
|
+
}
|
|
233
|
+
const n = Math.round(count);
|
|
234
|
+
if (n < 0) {
|
|
235
|
+
prev({ count: Math.abs(n), animated, onFinished });
|
|
236
|
+
} else {
|
|
237
|
+
next({ count: n, animated, onFinished });
|
|
238
|
+
}
|
|
239
|
+
},
|
|
240
|
+
[prev, next]
|
|
241
|
+
);
|
|
242
|
+
|
|
182
243
|
return {
|
|
183
244
|
next,
|
|
184
245
|
prev,
|
|
185
246
|
to,
|
|
247
|
+
scrollTo,
|
|
186
248
|
index,
|
|
187
249
|
length,
|
|
188
250
|
sharedIndex,
|
|
@@ -2,12 +2,20 @@ import React from 'react';
|
|
|
2
2
|
import { DATA_LENGTH } from '../constants';
|
|
3
3
|
import type { TCarouselProps } from '../types';
|
|
4
4
|
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
5
|
+
type TGetRequiredProps<P extends keyof TCarouselProps> = Record<
|
|
6
|
+
P,
|
|
7
|
+
Required<TCarouselProps>[P]
|
|
8
|
+
>;
|
|
9
|
+
|
|
10
|
+
export type TInitializeCarouselProps<T> = TCarouselProps<T> &
|
|
11
|
+
TGetRequiredProps<
|
|
12
|
+
| 'defaultIndex'
|
|
13
|
+
| 'loop'
|
|
14
|
+
| 'width'
|
|
15
|
+
| 'height'
|
|
16
|
+
| 'scrollAnimationDuration'
|
|
17
|
+
| 'autoPlayInterval'
|
|
18
|
+
>;
|
|
11
19
|
|
|
12
20
|
export function useInitProps<T>(
|
|
13
21
|
props: TCarouselProps<T>
|
|
@@ -16,7 +24,8 @@ export function useInitProps<T>(
|
|
|
16
24
|
defaultIndex = 0,
|
|
17
25
|
data: _data = [],
|
|
18
26
|
loop = true,
|
|
19
|
-
autoPlayInterval = 1000,
|
|
27
|
+
autoPlayInterval: _autoPlayInterval = 1000,
|
|
28
|
+
scrollAnimationDuration = 500,
|
|
20
29
|
style = {},
|
|
21
30
|
panGestureHandlerProps = {},
|
|
22
31
|
pagingEnabled = true,
|
|
@@ -27,6 +36,7 @@ export function useInitProps<T>(
|
|
|
27
36
|
|
|
28
37
|
const width = Math.round(_width || 0);
|
|
29
38
|
const height = Math.round(_height || 0);
|
|
39
|
+
const autoPlayInterval = Math.max(_autoPlayInterval, 0);
|
|
30
40
|
|
|
31
41
|
const data = React.useMemo<T[]>(() => {
|
|
32
42
|
if (!loop) return _data;
|
|
@@ -49,13 +59,13 @@ export function useInitProps<T>(
|
|
|
49
59
|
props.modeConfig.showLength =
|
|
50
60
|
props.modeConfig?.showLength ?? data.length - 1;
|
|
51
61
|
}
|
|
52
|
-
|
|
53
62
|
return {
|
|
54
63
|
...props,
|
|
55
64
|
defaultIndex,
|
|
56
65
|
data,
|
|
57
66
|
loop,
|
|
58
67
|
autoPlayInterval,
|
|
68
|
+
scrollAnimationDuration,
|
|
59
69
|
style,
|
|
60
70
|
panGestureHandlerProps,
|
|
61
71
|
pagingEnabled,
|
|
@@ -4,12 +4,13 @@ import Animated, {
|
|
|
4
4
|
runOnJS,
|
|
5
5
|
useAnimatedReaction,
|
|
6
6
|
useAnimatedStyle,
|
|
7
|
+
useDerivedValue,
|
|
7
8
|
} from 'react-native-reanimated';
|
|
8
9
|
import { IOpts, useOffsetX } from '../hooks/useOffsetX';
|
|
9
10
|
import type { IVisibleRanges } from '../hooks/useVisibleRanges';
|
|
10
11
|
import { LazyView } from '../LazyView';
|
|
11
|
-
import type { ILayoutConfig as IStackLayoutConfig } from './stack';
|
|
12
12
|
import { CTX } from '../store';
|
|
13
|
+
import type { ILayoutConfig } from './stack';
|
|
13
14
|
|
|
14
15
|
export type TAnimationStyle = (
|
|
15
16
|
value: number
|
|
@@ -20,28 +21,39 @@ export const BaseLayout: React.FC<{
|
|
|
20
21
|
handlerOffsetX: Animated.SharedValue<number>;
|
|
21
22
|
visibleRanges: IVisibleRanges;
|
|
22
23
|
animationStyle: TAnimationStyle;
|
|
24
|
+
children: (ctx: {
|
|
25
|
+
animationValue: Animated.SharedValue<number>;
|
|
26
|
+
}) => React.ReactElement;
|
|
23
27
|
}> = (props) => {
|
|
24
28
|
const { handlerOffsetX, index, children, visibleRanges, animationStyle } =
|
|
25
29
|
props;
|
|
26
30
|
|
|
31
|
+
const context = React.useContext(CTX);
|
|
27
32
|
const {
|
|
28
|
-
props: {
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
+
props: {
|
|
34
|
+
loop,
|
|
35
|
+
data,
|
|
36
|
+
width,
|
|
37
|
+
height,
|
|
38
|
+
vertical,
|
|
39
|
+
customConfig,
|
|
40
|
+
mode,
|
|
41
|
+
modeConfig,
|
|
42
|
+
},
|
|
43
|
+
} = context;
|
|
33
44
|
const size = vertical ? height : width;
|
|
34
|
-
|
|
45
|
+
const [shouldUpdate, setShouldUpdate] = React.useState(false);
|
|
35
46
|
let offsetXConfig: IOpts = {
|
|
36
47
|
handlerOffsetX,
|
|
37
48
|
index,
|
|
38
49
|
size,
|
|
39
50
|
data,
|
|
40
51
|
loop,
|
|
52
|
+
...(typeof customConfig === 'function' ? customConfig() : {}),
|
|
41
53
|
};
|
|
42
54
|
|
|
43
55
|
if (mode === 'horizontal-stack') {
|
|
44
|
-
const { snapDirection, showLength } = modeConfig as
|
|
56
|
+
const { snapDirection, showLength } = modeConfig as ILayoutConfig;
|
|
45
57
|
|
|
46
58
|
offsetXConfig = {
|
|
47
59
|
handlerOffsetX,
|
|
@@ -55,8 +67,8 @@ export const BaseLayout: React.FC<{
|
|
|
55
67
|
}
|
|
56
68
|
|
|
57
69
|
const x = useOffsetX(offsetXConfig, visibleRanges);
|
|
58
|
-
|
|
59
|
-
const
|
|
70
|
+
const animationValue = useDerivedValue(() => x.value / size, [x, size]);
|
|
71
|
+
const animatedStyle = useAnimatedStyle(
|
|
60
72
|
() => animationStyle(x.value / size),
|
|
61
73
|
[animationStyle]
|
|
62
74
|
);
|
|
@@ -90,10 +102,12 @@ export const BaseLayout: React.FC<{
|
|
|
90
102
|
height: height || '100%',
|
|
91
103
|
position: 'absolute',
|
|
92
104
|
},
|
|
93
|
-
|
|
105
|
+
animatedStyle,
|
|
94
106
|
]}
|
|
95
107
|
>
|
|
96
|
-
<LazyView shouldUpdate={shouldUpdate}>
|
|
108
|
+
<LazyView shouldUpdate={shouldUpdate}>
|
|
109
|
+
{children({ animationValue })}
|
|
110
|
+
</LazyView>
|
|
97
111
|
</Animated.View>
|
|
98
112
|
);
|
|
99
113
|
};
|
package/src/layouts/normal.ts
CHANGED
|
@@ -1,16 +1,11 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { interpolate } from 'react-native-reanimated';
|
|
2
2
|
|
|
3
3
|
export function normalLayout(opts: { size: number; vertical: boolean }) {
|
|
4
4
|
const { size, vertical } = opts;
|
|
5
5
|
|
|
6
6
|
return (value: number) => {
|
|
7
7
|
'worklet';
|
|
8
|
-
const translate = interpolate(
|
|
9
|
-
value,
|
|
10
|
-
[-1, 0, 1],
|
|
11
|
-
[-size, 0, size],
|
|
12
|
-
Extrapolate.CLAMP
|
|
13
|
-
);
|
|
8
|
+
const translate = interpolate(value, [-1, 0, 1], [-size, 0, size]);
|
|
14
9
|
|
|
15
10
|
return {
|
|
16
11
|
transform: [
|
package/src/layouts/parallax.ts
CHANGED
|
@@ -1,11 +1,12 @@
|
|
|
1
1
|
import { Extrapolate, interpolate } from 'react-native-reanimated';
|
|
2
|
+
import type { ComputedDirectionTypes } from '../types';
|
|
2
3
|
|
|
3
4
|
type TBaseConfig = {
|
|
4
5
|
size: number;
|
|
5
6
|
vertical: boolean;
|
|
6
7
|
};
|
|
7
8
|
|
|
8
|
-
|
|
9
|
+
interface ILayoutConfig {
|
|
9
10
|
/**
|
|
10
11
|
* When use default Layout props,this prop can be control prev/next item offset.
|
|
11
12
|
* @default 100
|
|
@@ -18,6 +19,14 @@ export interface ILayoutConfig {
|
|
|
18
19
|
parallaxScrollingScale?: number;
|
|
19
20
|
}
|
|
20
21
|
|
|
22
|
+
export type TParallaxModeProps = ComputedDirectionTypes<{
|
|
23
|
+
/**
|
|
24
|
+
* Carousel Animated transitions.
|
|
25
|
+
*/
|
|
26
|
+
mode?: 'parallax';
|
|
27
|
+
modeConfig?: ILayoutConfig;
|
|
28
|
+
}>;
|
|
29
|
+
|
|
21
30
|
export function parallaxLayout(
|
|
22
31
|
baseConfig: TBaseConfig,
|
|
23
32
|
modeConfig: ILayoutConfig = {}
|
package/src/layouts/stack.ts
CHANGED
|
@@ -1,5 +1,7 @@
|
|
|
1
|
+
import { useMemo } from 'react';
|
|
1
2
|
import { Dimensions, TransformsStyle, ViewStyle } from 'react-native';
|
|
2
3
|
import { Extrapolate, interpolate } from 'react-native-reanimated';
|
|
4
|
+
import type { ComputedDirectionTypes, CustomConfig } from 'src/types';
|
|
3
5
|
|
|
4
6
|
const screen = Dimensions.get('window');
|
|
5
7
|
|
|
@@ -13,6 +15,24 @@ export interface ILayoutConfig {
|
|
|
13
15
|
snapDirection?: 'left' | 'right';
|
|
14
16
|
}
|
|
15
17
|
|
|
18
|
+
export type TStackModeProps = ComputedDirectionTypes<{
|
|
19
|
+
/**
|
|
20
|
+
* Carousel Animated transitions.
|
|
21
|
+
*/
|
|
22
|
+
mode?: 'horizontal-stack' | 'vertical-stack';
|
|
23
|
+
/**
|
|
24
|
+
* Stack animation style.
|
|
25
|
+
* @default
|
|
26
|
+
* mode: 'vertical',
|
|
27
|
+
* snapDirection: 'right',
|
|
28
|
+
* moveSize: window.width,
|
|
29
|
+
* stackInterval: 30,
|
|
30
|
+
* scaleInterval: 0.08,
|
|
31
|
+
* rotateZDeg: 135,
|
|
32
|
+
*/
|
|
33
|
+
modeConfig?: ILayoutConfig;
|
|
34
|
+
}>;
|
|
35
|
+
|
|
16
36
|
export function horizontalStackLayout(modeConfig: ILayoutConfig = {}) {
|
|
17
37
|
return (_value: number) => {
|
|
18
38
|
'worklet';
|
|
@@ -106,6 +126,28 @@ export function horizontalStackLayout(modeConfig: ILayoutConfig = {}) {
|
|
|
106
126
|
};
|
|
107
127
|
}
|
|
108
128
|
|
|
129
|
+
export function useHorizontalStackLayout(
|
|
130
|
+
customAnimationConfig: ILayoutConfig = {},
|
|
131
|
+
customConfig: CustomConfig = {}
|
|
132
|
+
) {
|
|
133
|
+
const config = useMemo(
|
|
134
|
+
() => ({
|
|
135
|
+
type:
|
|
136
|
+
customAnimationConfig.snapDirection === 'right'
|
|
137
|
+
? 'negative'
|
|
138
|
+
: 'positive',
|
|
139
|
+
viewCount: customAnimationConfig.showLength,
|
|
140
|
+
...customConfig,
|
|
141
|
+
}),
|
|
142
|
+
[customAnimationConfig, customConfig]
|
|
143
|
+
);
|
|
144
|
+
|
|
145
|
+
return {
|
|
146
|
+
layout: horizontalStackLayout(customAnimationConfig),
|
|
147
|
+
config,
|
|
148
|
+
};
|
|
149
|
+
}
|
|
150
|
+
|
|
109
151
|
export function verticalStackLayout(modeConfig: ILayoutConfig = {}) {
|
|
110
152
|
return (_value: number) => {
|
|
111
153
|
'worklet';
|