@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.
- package/Carousel.js +242 -263
- package/CarouselV2.js +1363 -0
- package/index.js +3 -4
- package/package.json +15 -14
- package/pagination/NumberPagination.js +43 -0
- package/pagination/Pagination.js +64 -34
- package/pagination/PaginationDot.js +45 -39
- package/pagination/styles.js +6 -6
- package/publish.sh +1 -1
- package/utils/animationsV2.js +381 -0
|
@@ -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
|
+
}
|