@momo-kits/carousel 0.0.70-beta → 0.0.71-beta.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.
@@ -0,0 +1,381 @@
1
+ import { Platform, Animated } from 'react-native';
2
+ const IS_ANDROID = Platform.OS === 'android';
3
+
4
+ // Get scroll interpolator's input range from an array of slide indexes
5
+ // Indexes are relative to the current active slide (index 0)
6
+ // For example, using [3, 2, 1, 0, -1] will return:
7
+ // [
8
+ // (index - 3) * sizeRef, // active + 3
9
+ // (index - 2) * sizeRef, // active + 2
10
+ // (index - 1) * sizeRef, // active + 1
11
+ // index * sizeRef, // active
12
+ // (index + 1) * sizeRef // active - 1
13
+ // ]
14
+ export function getInputRangeFromIndexes (
15
+ range,
16
+ index,
17
+ carouselProps
18
+ ) {
19
+ const sizeRef = carouselProps.vertical ?
20
+ carouselProps.itemHeight :
21
+ carouselProps.itemWidth;
22
+ const inputRange = [];
23
+
24
+ for (let i = 0; i < range.length; i++) {
25
+ inputRange.push((index - range[i]) * sizeRef);
26
+ }
27
+
28
+ return inputRange;
29
+ }
30
+
31
+ // Default behavior
32
+ // Scale and/or opacity effect
33
+ // Based on props 'inactiveSlideOpacity' and 'inactiveSlideScale'
34
+ export function defaultScrollInterpolator (
35
+ index,
36
+ carouselProps
37
+ ) {
38
+ const range = [1, 0, -1];
39
+ const inputRange = getInputRangeFromIndexes(range, index, carouselProps);
40
+ const outputRange = [0, 1, 0];
41
+
42
+ return { inputRange, outputRange };
43
+ }
44
+ export function defaultAnimatedStyles (
45
+ _index,
46
+ animatedValue,
47
+ carouselProps
48
+ ) {
49
+ let animatedOpacity = {};
50
+ let animatedScale = {};
51
+
52
+ if (carouselProps.inactiveSlideOpacity < 1) {
53
+ animatedOpacity = {
54
+ opacity: animatedValue.interpolate({
55
+ inputRange: [0, 1],
56
+ outputRange: [carouselProps.inactiveSlideOpacity, 1]
57
+ })
58
+ };
59
+ }
60
+
61
+ if (carouselProps.inactiveSlideScale < 1) {
62
+ animatedScale = {
63
+ transform: [
64
+ {
65
+ scale: animatedValue.interpolate({
66
+ inputRange: [0, 1],
67
+ outputRange: [carouselProps.inactiveSlideScale, 1]
68
+ })
69
+ }
70
+ ]
71
+ };
72
+ }
73
+
74
+ return {
75
+ ...animatedOpacity,
76
+ ...animatedScale
77
+ };
78
+ }
79
+
80
+ // Shift animation
81
+ // Same as the default one, but the active slide is also shifted up or down
82
+ // Based on prop 'inactiveSlideShift'
83
+ export function shiftAnimatedStyles (
84
+ _index,
85
+ animatedValue,
86
+ carouselProps
87
+ ) {
88
+ let animatedOpacity = {};
89
+ let animatedScale = {};
90
+ let animatedTranslate = {};
91
+
92
+ if (carouselProps.inactiveSlideOpacity < 1) {
93
+ animatedOpacity = {
94
+ opacity: animatedValue.interpolate({
95
+ inputRange: [0, 1],
96
+ outputRange: [carouselProps.inactiveSlideOpacity, 1]
97
+ })
98
+ };
99
+ }
100
+
101
+ if (carouselProps.inactiveSlideScale < 1) {
102
+ animatedScale = {
103
+ scale: animatedValue.interpolate({
104
+ inputRange: [0, 1],
105
+ outputRange: [carouselProps.inactiveSlideScale, 1]
106
+ })
107
+ };
108
+ }
109
+
110
+ if (carouselProps.inactiveSlideShift !== 0) {
111
+ const translateProp = carouselProps.vertical ? 'translateX' : 'translateY';
112
+ animatedTranslate = {
113
+ [translateProp]: animatedValue.interpolate({
114
+ inputRange: [0, 1],
115
+ outputRange: [carouselProps.inactiveSlideShift, 0]
116
+ })
117
+ };
118
+ }
119
+
120
+ return {
121
+ ...animatedOpacity,
122
+ transform: [{ ...animatedScale }, { ...animatedTranslate }]
123
+ };
124
+ }
125
+
126
+ // Stack animation
127
+ // Imitate a deck/stack of cards (see #195)
128
+ // WARNING: The effect had to be visually inverted on Android because this OS doesn't honor the `zIndex`property
129
+ // This means that the item with the higher zIndex (and therefore the tap receiver) remains the one AFTER the currently active item
130
+ // The `elevation` property compensates for that only visually, which is not good enough
131
+ export function stackScrollInterpolator (
132
+ index,
133
+ carouselProps
134
+ ) {
135
+ const range = IS_ANDROID ? [1, 0, -1, -2, -3] : [3, 2, 1, 0, -1];
136
+ const inputRange = getInputRangeFromIndexes(range, index, carouselProps);
137
+ const outputRange = range;
138
+
139
+ return { inputRange, outputRange };
140
+ }
141
+ export function stackAnimatedStyles (
142
+ index,
143
+ animatedValue,
144
+ carouselProps,
145
+ cardOffset
146
+ ) {
147
+ const sizeRef = carouselProps.vertical ?
148
+ carouselProps.itemHeight :
149
+ carouselProps.itemWidth;
150
+ const translateProp = carouselProps.vertical ? 'translateY' : 'translateX';
151
+
152
+ const card1Scale = 0.9;
153
+ const card2Scale = 0.8;
154
+
155
+ const newCardOffset = cardOffset ?? 18;
156
+
157
+ const getTranslateFromScale = (cardIndex, scale) => {
158
+ const centerFactor = (1 / scale) * cardIndex;
159
+ const centeredPosition = -Math.round(sizeRef * centerFactor);
160
+ const edgeAlignment = Math.round((sizeRef - sizeRef * scale) / 2);
161
+ const offset = Math.round((newCardOffset * Math.abs(cardIndex)) / scale);
162
+
163
+ return IS_ANDROID ?
164
+ centeredPosition - edgeAlignment - offset :
165
+ centeredPosition + edgeAlignment + offset;
166
+ };
167
+
168
+ const opacityOutputRange =
169
+ carouselProps.inactiveSlideOpacity === 1 ? [1, 1, 1, 0] : [1, 0.75, 0.5, 0];
170
+
171
+ return IS_ANDROID ?
172
+ {
173
+ // elevation.data.length - index, // fix zIndex bug visually, but not from a logic point of view
174
+ opacity: animatedValue.interpolate({
175
+ inputRange: [-3, -2, -1, 0],
176
+ outputRange: opacityOutputRange.reverse(),
177
+ extrapolate: 'clamp'
178
+ }),
179
+ transform: [
180
+ {
181
+ scale: animatedValue.interpolate({
182
+ inputRange: [-2, -1, 0, 1],
183
+ outputRange: [card2Scale, card1Scale, 1, card1Scale],
184
+ extrapolate: 'clamp'
185
+ })
186
+ },
187
+ {
188
+ [translateProp]: animatedValue.interpolate({
189
+ inputRange: [-3, -2, -1, 0, 1],
190
+ outputRange: [
191
+ getTranslateFromScale(-3, card2Scale),
192
+ getTranslateFromScale(-2, card2Scale),
193
+ getTranslateFromScale(-1, card1Scale),
194
+ 0,
195
+ sizeRef * 0.5
196
+ ],
197
+ extrapolate: 'clamp'
198
+ })
199
+ }
200
+ ]
201
+ } :
202
+ {
203
+ zIndex: carouselProps.data.length - index,
204
+ opacity: animatedValue.interpolate({
205
+ inputRange: [0, 1, 2, 3],
206
+ outputRange: opacityOutputRange,
207
+ extrapolate: 'clamp'
208
+ }),
209
+ transform: [
210
+ {
211
+ scale: animatedValue.interpolate({
212
+ inputRange: [-1, 0, 1, 2],
213
+ outputRange: [card1Scale, 1, card1Scale, card2Scale],
214
+ extrapolate: 'clamp'
215
+ })
216
+ },
217
+ {
218
+ [translateProp]: animatedValue.interpolate({
219
+ inputRange: [-1, 0, 1, 2, 3],
220
+ outputRange: [
221
+ -sizeRef * 0.5,
222
+ 0,
223
+ getTranslateFromScale(1, card1Scale),
224
+ getTranslateFromScale(2, card2Scale),
225
+ getTranslateFromScale(3, card2Scale)
226
+ ],
227
+ extrapolate: 'clamp'
228
+ })
229
+ }
230
+ ]
231
+ };
232
+ }
233
+
234
+ // Tinder animation
235
+ // Imitate the popular Tinder layout
236
+ // WARNING: The effect had to be visually inverted on Android because this OS doesn't honor the `zIndex`property
237
+ // This means that the item with the higher zIndex (and therefore the tap receiver) remains the one AFTER the currently active item
238
+ // The `elevation` property compensates for that only visually, which is not good enough
239
+ export function tinderScrollInterpolator (
240
+ index,
241
+ carouselProps
242
+ ) {
243
+ const range = IS_ANDROID ? [1, 0, -1, -2, -3] : [3, 2, 1, 0, -1];
244
+ const inputRange = getInputRangeFromIndexes(range, index, carouselProps);
245
+ const outputRange = range;
246
+
247
+ return { inputRange, outputRange };
248
+ }
249
+ export function tinderAnimatedStyles (
250
+ index,
251
+ animatedValue,
252
+ carouselProps,
253
+ cardOffset
254
+ ) {
255
+ const sizeRef = carouselProps.vertical ?
256
+ carouselProps.itemHeight :
257
+ carouselProps.itemWidth;
258
+ const mainTranslateProp = carouselProps.vertical ?
259
+ 'translateY' :
260
+ 'translateX';
261
+ const secondaryTranslateProp = carouselProps.vertical ?
262
+ 'translateX' :
263
+ 'translateY';
264
+
265
+ const card1Scale = 0.96;
266
+ const card2Scale = 0.92;
267
+ const card3Scale = 0.88;
268
+
269
+ const peekingCardsOpacity = IS_ANDROID ? 0.92 : 1;
270
+
271
+ const newCardOffset = cardOffset ?? 9;
272
+
273
+ const getMainTranslateFromScale = (cardIndex, scale) => {
274
+ const centerFactor = (1 / scale) * cardIndex;
275
+ return -Math.round(sizeRef * centerFactor);
276
+ };
277
+
278
+ const getSecondaryTranslateFromScale = (cardIndex, scale) => {
279
+ return Math.round((newCardOffset * Math.abs(cardIndex)) / scale);
280
+ };
281
+
282
+ return IS_ANDROID ?
283
+ {
284
+ // elevation.data.length - index, // fix zIndex bug visually, but not from a logic point of view
285
+ opacity: animatedValue.interpolate({
286
+ inputRange: [-3, -2, -1, 0, 1],
287
+ outputRange: [0, peekingCardsOpacity, peekingCardsOpacity, 1, 0],
288
+ extrapolate: 'clamp'
289
+ }),
290
+ transform: [
291
+ {
292
+ scale: animatedValue.interpolate({
293
+ inputRange: [-3, -2, -1, 0],
294
+ outputRange: [card3Scale, card2Scale, card1Scale, 1],
295
+ extrapolate: 'clamp'
296
+ })
297
+ },
298
+ {
299
+ rotate: animatedValue.interpolate({
300
+ inputRange: [0, 1],
301
+ outputRange: ['0deg', '22deg'],
302
+ extrapolate: 'clamp'
303
+ })
304
+ },
305
+ {
306
+ [mainTranslateProp]: animatedValue.interpolate({
307
+ inputRange: [-3, -2, -1, 0, 1],
308
+ outputRange: [
309
+ getMainTranslateFromScale(-3, card3Scale),
310
+ getMainTranslateFromScale(-2, card2Scale),
311
+ getMainTranslateFromScale(-1, card1Scale),
312
+ 0,
313
+ sizeRef * 1.1
314
+ ],
315
+ extrapolate: 'clamp'
316
+ })
317
+ },
318
+ {
319
+ [secondaryTranslateProp]: animatedValue.interpolate({
320
+ inputRange: [-3, -2, -1, 0],
321
+ outputRange: [
322
+ getSecondaryTranslateFromScale(-3, card3Scale),
323
+ getSecondaryTranslateFromScale(-2, card2Scale),
324
+ getSecondaryTranslateFromScale(-1, card1Scale),
325
+ 0
326
+ ],
327
+ extrapolate: 'clamp'
328
+ })
329
+ }
330
+ ]
331
+ } :
332
+ {
333
+ zIndex: carouselProps.data.length - index,
334
+ opacity: animatedValue.interpolate({
335
+ inputRange: [-1, 0, 1, 2, 3],
336
+ outputRange: [0, 1, peekingCardsOpacity, peekingCardsOpacity, 0],
337
+ extrapolate: 'clamp'
338
+ }),
339
+ transform: [
340
+ {
341
+ scale: animatedValue.interpolate({
342
+ inputRange: [0, 1, 2, 3],
343
+ outputRange: [1, card1Scale, card2Scale, card3Scale],
344
+ extrapolate: 'clamp'
345
+ })
346
+ },
347
+ {
348
+ rotate: animatedValue.interpolate({
349
+ inputRange: [-1, 0],
350
+ outputRange: ['-22deg', '0deg'],
351
+ extrapolate: 'clamp'
352
+ })
353
+ },
354
+ {
355
+ [mainTranslateProp]: animatedValue.interpolate({
356
+ inputRange: [-1, 0, 1, 2, 3],
357
+ outputRange: [
358
+ -sizeRef * 1.1,
359
+ 0,
360
+ getMainTranslateFromScale(1, card1Scale),
361
+ getMainTranslateFromScale(2, card2Scale),
362
+ getMainTranslateFromScale(3, card3Scale)
363
+ ],
364
+ extrapolate: 'clamp'
365
+ })
366
+ },
367
+ {
368
+ [secondaryTranslateProp]: animatedValue.interpolate({
369
+ inputRange: [0, 1, 2, 3],
370
+ outputRange: [
371
+ 0,
372
+ getSecondaryTranslateFromScale(1, card1Scale),
373
+ getSecondaryTranslateFromScale(2, card2Scale),
374
+ getSecondaryTranslateFromScale(3, card3Scale)
375
+ ],
376
+ extrapolate: 'clamp'
377
+ })
378
+ }
379
+ ]
380
+ };
381
+ }