@momo-kits/carousel 0.0.65-alpha.2 → 0.0.65-alpha.22

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.
Files changed (3) hide show
  1. package/CarouselV2.js +1223 -1193
  2. package/package.json +15 -15
  3. package/utils/animationsV2.js +320 -279
package/CarouselV2.js CHANGED
@@ -1,14 +1,14 @@
1
1
  import React from 'react';
2
- import {Animated, I18nManager, Platform, View} from 'react-native';
2
+ import { Animated, I18nManager, Platform, View } from 'react-native';
3
3
  // import shallowCompare from 'react-addons-shallow-compare';
4
4
  import {
5
- defaultScrollInterpolator,
6
- stackScrollInterpolator,
7
- tinderScrollInterpolator,
8
- defaultAnimatedStyles,
9
- shiftAnimatedStyles,
10
- stackAnimatedStyles,
11
- tinderAnimatedStyles,
5
+ defaultScrollInterpolator,
6
+ stackScrollInterpolator,
7
+ tinderScrollInterpolator,
8
+ defaultAnimatedStyles,
9
+ shiftAnimatedStyles,
10
+ stackAnimatedStyles,
11
+ tinderAnimatedStyles,
12
12
  } from './utils/animationsV2';
13
13
  import PropTypes from 'prop-types';
14
14
 
@@ -27,1337 +27,1367 @@ const IS_IOS = Platform.OS === 'ios';
27
27
  const IS_RTL = I18nManager.isRTL;
28
28
 
29
29
  export default class Carousel extends React.PureComponent {
30
- constructor(props) {
31
- super(props);
32
-
33
- this.state = {
34
- hideCarousel: !!props.apparitionDelay,
35
- interpolators: [],
36
- };
37
-
38
- // this._RNVersionCode = this._getRNVersionCode();
39
-
40
- // The following values are not stored in the state because 'setState()' is asynchronous
41
- // and this results in an absolutely crappy behavior on Android while swiping (see #156)
42
- const initialActiveItem = this._getFirstItem(props.firstItem);
43
- this._activeItem = initialActiveItem;
44
- this._onScrollActiveItem = initialActiveItem;
45
- this._previousFirstItem = initialActiveItem;
46
- this._previousItemsLength = initialActiveItem;
47
-
48
- this._mounted = false;
49
- this._positions = [];
50
- this._currentScrollOffset = 0; // Store ScrollView's scroll position
51
- this._scrollEnabled = props.scrollEnabled !== false;
52
-
53
- this._getCellRendererComponent = this._getCellRendererComponent.bind(this);
54
- this._getItemLayout = this._getItemLayout.bind(this);
55
- this._getKeyExtractor = this._getKeyExtractor.bind(this);
56
- this._onLayout = this._onLayout.bind(this);
57
- this._onScroll = this._onScroll.bind(this);
58
- this._onMomentumScrollEnd = this._onMomentumScrollEnd.bind(this);
59
- this._onTouchStart = this._onTouchStart.bind(this);
60
- this._onTouchEnd = this._onTouchEnd.bind(this);
61
- this._renderItem = this._renderItem.bind(this);
62
-
63
- // WARNING: call this AFTER binding _onScroll
64
- this._setScrollHandler(props);
65
-
66
- // Display warnings
67
- this._displayWarnings(props);
68
- }
69
-
70
- get realIndex() {
71
- return this._activeItem;
72
- }
73
-
74
- // shouldComponentUpdate (
75
- // nextProps,
76
- // nextState
77
- // ) {
78
- // if (this.props.shouldOptimizeUpdates === false) {
79
- // return true;
80
- // } else {
81
- // return shallowCompare(this, nextProps, nextState);
82
- // }
83
- // }
84
-
85
- get currentIndex() {
86
- return this._getDataIndex(this._activeItem);
87
- }
88
-
89
- get currentScrollPosition() {
90
- return this._currentScrollOffset;
91
- }
92
-
93
- componentDidMount() {
94
- const {apparitionDelay, autoplay, firstItem} = this.props;
95
-
96
- this._mounted = true;
97
- this._initPositionsAndInterpolators();
98
-
99
- // Without 'requestAnimationFrame' or a `0` timeout, images will randomly not be rendered on Android...
100
- this._initTimeout = setTimeout(() => {
101
- if (!this._mounted) {
102
- return;
103
- }
104
-
105
- const apparitionCallback = () => {
106
- if (apparitionDelay) {
107
- this.setState({hideCarousel: false});
30
+ constructor(props) {
31
+ super(props);
32
+
33
+ this.state = {
34
+ hideCarousel: !!props.apparitionDelay,
35
+ interpolators: [],
36
+ };
37
+
38
+ // this._RNVersionCode = this._getRNVersionCode();
39
+
40
+ // The following values are not stored in the state because 'setState()' is asynchronous
41
+ // and this results in an absolutely crappy behavior on Android while swiping (see #156)
42
+ const initialActiveItem = this._getFirstItem(props.firstItem);
43
+ this._activeItem = initialActiveItem;
44
+ this._onScrollActiveItem = initialActiveItem;
45
+ this._previousFirstItem = initialActiveItem;
46
+ this._previousItemsLength = initialActiveItem;
47
+
48
+ this._mounted = false;
49
+ this._positions = [];
50
+ this._currentScrollOffset = 0; // Store ScrollView's scroll position
51
+ this._scrollEnabled = props.scrollEnabled !== false;
52
+
53
+ this._getCellRendererComponent =
54
+ this._getCellRendererComponent.bind(this);
55
+ this._getItemLayout = this._getItemLayout.bind(this);
56
+ this._getKeyExtractor = this._getKeyExtractor.bind(this);
57
+ this._onLayout = this._onLayout.bind(this);
58
+ this._onScroll = this._onScroll.bind(this);
59
+ this._onMomentumScrollEnd = this._onMomentumScrollEnd.bind(this);
60
+ this._onTouchStart = this._onTouchStart.bind(this);
61
+ this._onTouchEnd = this._onTouchEnd.bind(this);
62
+ this._renderItem = this._renderItem.bind(this);
63
+
64
+ // WARNING: call this AFTER binding _onScroll
65
+ this._setScrollHandler(props);
66
+
67
+ // Display warnings
68
+ this._displayWarnings(props);
69
+ }
70
+
71
+ componentDidMount() {
72
+ const { apparitionDelay, autoplay, firstItem } = this.props;
73
+
74
+ this._mounted = true;
75
+ this._initPositionsAndInterpolators();
76
+
77
+ // Without 'requestAnimationFrame' or a `0` timeout, images will randomly not be rendered on Android...
78
+ this._initTimeout = setTimeout(() => {
79
+ if (!this._mounted) {
80
+ return;
81
+ }
82
+
83
+ const apparitionCallback = () => {
84
+ if (apparitionDelay) {
85
+ this.setState({ hideCarousel: false });
86
+ }
87
+ if (autoplay) {
88
+ this.startAutoplay();
89
+ }
90
+ };
91
+
92
+ // FlatList will use its own built-in prop `initialScrollIndex`
93
+ if (this._needsScrollView()) {
94
+ const _firstItem = this._getFirstItem(firstItem);
95
+ this._snapToItem(_firstItem, false, false, true);
96
+ // this._hackActiveSlideAnimation(_firstItem);
97
+ }
98
+
99
+ if (apparitionDelay) {
100
+ this._apparitionTimeout = setTimeout(() => {
101
+ apparitionCallback();
102
+ }, apparitionDelay);
103
+ } else {
104
+ apparitionCallback();
105
+ }
106
+ }, 1);
107
+ }
108
+
109
+ // shouldComponentUpdate (
110
+ // nextProps,
111
+ // nextState
112
+ // ) {
113
+ // if (this.props.shouldOptimizeUpdates === false) {
114
+ // return true;
115
+ // } else {
116
+ // return shallowCompare(this, nextProps, nextState);
117
+ // }
118
+ // }
119
+
120
+ componentDidUpdate(prevProps) {
121
+ const { interpolators } = this.state;
122
+ const { firstItem, scrollEnabled } = this.props;
123
+ const itemsLength = this._getCustomDataLength(this.props);
124
+
125
+ if (!itemsLength) {
126
+ return;
127
+ }
128
+
129
+ const nextFirstItem = this._getFirstItem(firstItem, this.props);
130
+ let nextActiveItem =
131
+ typeof this._activeItem !== 'undefined'
132
+ ? this._activeItem
133
+ : nextFirstItem;
134
+
135
+ const hasNewSize =
136
+ this.props.vertical !== prevProps.vertical ||
137
+ (this.props.vertical &&
138
+ prevProps.vertical &&
139
+ (prevProps.itemHeight !== this.props.itemHeight ||
140
+ prevProps.sliderHeight !== this.props.sliderHeight)) ||
141
+ (!this.props.vertical &&
142
+ !prevProps.vertical &&
143
+ (prevProps.itemWidth !== this.props.itemWidth ||
144
+ prevProps.sliderWidth !== this.props.sliderWidth));
145
+
146
+ // Prevent issues with dynamically removed items
147
+ if (nextActiveItem > itemsLength - 1) {
148
+ nextActiveItem = itemsLength - 1;
149
+ }
150
+
151
+ // Handle changing scrollEnabled independent of user -> carousel interaction
152
+ if (scrollEnabled !== prevProps.scrollEnabled) {
153
+ this._setScrollEnabled(scrollEnabled);
154
+ }
155
+
156
+ if (interpolators.length !== itemsLength || hasNewSize) {
157
+ this._activeItem = nextActiveItem;
158
+ this._previousItemsLength = itemsLength;
159
+
160
+ this._initPositionsAndInterpolators(this.props);
161
+
162
+ // Handle scroll issue when dynamically removing items (see #133)
163
+ // This also fixes first item's active state on Android
164
+ // Because 'initialScrollIndex' apparently doesn't trigger scroll
165
+ if (this._previousItemsLength > itemsLength) {
166
+ this._hackActiveSlideAnimation(nextActiveItem);
167
+ }
168
+
169
+ if (hasNewSize) {
170
+ this._snapToItem(nextActiveItem, false, false, true);
171
+ }
172
+ } else if (
173
+ nextFirstItem !== this._previousFirstItem &&
174
+ nextFirstItem !== this._activeItem
175
+ ) {
176
+ this._activeItem = nextFirstItem;
177
+ this._previousFirstItem = nextFirstItem;
178
+ this._snapToItem(nextFirstItem, false, true, true);
108
179
  }
109
- if (autoplay) {
110
- this.startAutoplay();
180
+
181
+ if (this.props.onScroll !== prevProps.onScroll) {
182
+ this._setScrollHandler(this.props);
111
183
  }
112
- };
113
-
114
- // FlatList will use its own built-in prop `initialScrollIndex`
115
- if (this._needsScrollView()) {
116
- const _firstItem = this._getFirstItem(firstItem);
117
- this._snapToItem(_firstItem, false, false, true);
118
- // this._hackActiveSlideAnimation(_firstItem);
119
- }
120
-
121
- if (apparitionDelay) {
122
- this._apparitionTimeout = setTimeout(() => {
123
- apparitionCallback();
124
- }, apparitionDelay);
125
- } else {
126
- apparitionCallback();
127
- }
128
- }, 1);
129
- }
130
-
131
- componentDidUpdate(prevProps) {
132
- const {interpolators} = this.state;
133
- const {firstItem, scrollEnabled} = this.props;
134
- const itemsLength = this._getCustomDataLength(this.props);
135
-
136
- if (!itemsLength) {
137
- return;
138
184
  }
139
185
 
140
- const nextFirstItem = this._getFirstItem(firstItem, this.props);
141
- let nextActiveItem =
142
- typeof this._activeItem !== 'undefined'
143
- ? this._activeItem
144
- : nextFirstItem;
145
-
146
- const hasNewSize =
147
- this.props.vertical !== prevProps.vertical ||
148
- (this.props.vertical &&
149
- prevProps.vertical &&
150
- (prevProps.itemHeight !== this.props.itemHeight ||
151
- prevProps.sliderHeight !== this.props.sliderHeight)) ||
152
- (!this.props.vertical &&
153
- !prevProps.vertical &&
154
- (prevProps.itemWidth !== this.props.itemWidth ||
155
- prevProps.sliderWidth !== this.props.sliderWidth));
156
-
157
- // Prevent issues with dynamically removed items
158
- if (nextActiveItem > itemsLength - 1) {
159
- nextActiveItem = itemsLength - 1;
186
+ componentWillUnmount() {
187
+ this._mounted = false;
188
+ this.stopAutoplay();
189
+ // @ts-expect-error setTimeout / clearTiemout is buggy :/
190
+ clearTimeout(this._initTimeout);
191
+ // @ts-expect-error setTimeout / clearTiemout is buggy :/
192
+ clearTimeout(this._apparitionTimeout);
193
+ // @ts-expect-error setTimeout / clearTiemout is buggy :/
194
+ clearTimeout(this._hackSlideAnimationTimeout);
195
+ // @ts-expect-error setTimeout / clearTiemout is buggy :/
196
+ clearTimeout(this._enableAutoplayTimeout);
197
+ // @ts-expect-error setTimeout / clearTiemout is buggy :/
198
+ clearTimeout(this._autoplayTimeout);
199
+ // @ts-expect-error setTimeout / clearTiemout is buggy :/
200
+ clearTimeout(this._snapNoMomentumTimeout);
201
+ // @ts-expect-error setTimeout / clearTiemout is buggy :/
202
+ clearTimeout(this._androidRepositioningTimeout);
160
203
  }
161
204
 
162
- // Handle changing scrollEnabled independent of user -> carousel interaction
163
- if (scrollEnabled !== prevProps.scrollEnabled) {
164
- this._setScrollEnabled(scrollEnabled);
205
+ get realIndex() {
206
+ return this._activeItem;
165
207
  }
166
208
 
167
- if (interpolators.length !== itemsLength || hasNewSize) {
168
- this._activeItem = nextActiveItem;
169
- this._previousItemsLength = itemsLength;
170
-
171
- this._initPositionsAndInterpolators(this.props);
172
-
173
- // Handle scroll issue when dynamically removing items (see #133)
174
- // This also fixes first item's active state on Android
175
- // Because 'initialScrollIndex' apparently doesn't trigger scroll
176
- if (this._previousItemsLength > itemsLength) {
177
- this._hackActiveSlideAnimation(nextActiveItem);
178
- }
179
-
180
- if (hasNewSize) {
181
- this._snapToItem(nextActiveItem, false, false, true);
182
- }
183
- } else if (
184
- nextFirstItem !== this._previousFirstItem &&
185
- nextFirstItem !== this._activeItem
186
- ) {
187
- this._activeItem = nextFirstItem;
188
- this._previousFirstItem = nextFirstItem;
189
- this._snapToItem(nextFirstItem, false, true, true);
209
+ get currentIndex() {
210
+ return this._getDataIndex(this._activeItem);
190
211
  }
191
212
 
192
- if (this.props.onScroll !== prevProps.onScroll) {
193
- this._setScrollHandler(this.props);
213
+ get currentScrollPosition() {
214
+ return this._currentScrollOffset;
194
215
  }
195
- }
196
-
197
- componentWillUnmount() {
198
- this._mounted = false;
199
- this.stopAutoplay();
200
- // @ts-expect-error setTimeout / clearTiemout is buggy :/
201
- clearTimeout(this._initTimeout);
202
- // @ts-expect-error setTimeout / clearTiemout is buggy :/
203
- clearTimeout(this._apparitionTimeout);
204
- // @ts-expect-error setTimeout / clearTiemout is buggy :/
205
- clearTimeout(this._hackSlideAnimationTimeout);
206
- // @ts-expect-error setTimeout / clearTiemout is buggy :/
207
- clearTimeout(this._enableAutoplayTimeout);
208
- // @ts-expect-error setTimeout / clearTiemout is buggy :/
209
- clearTimeout(this._autoplayTimeout);
210
- // @ts-expect-error setTimeout / clearTiemout is buggy :/
211
- clearTimeout(this._snapNoMomentumTimeout);
212
- // @ts-expect-error setTimeout / clearTiemout is buggy :/
213
- clearTimeout(this._androidRepositioningTimeout);
214
- }
215
-
216
- _setScrollHandler(props) {
217
- // Native driver for scroll events
218
- const scrollEventConfig = {
219
- listener: this._onScroll,
220
- useNativeDriver: true,
221
- };
222
- this._scrollPos = new Animated.Value(0);
223
- const argMapping = props.vertical
224
- ? [{nativeEvent: {contentOffset: {y: this._scrollPos}}}]
225
- : [{nativeEvent: {contentOffset: {x: this._scrollPos}}}];
226
-
227
- // @ts-expect-error Let's ignore for now that trick
228
- if (props.onScroll && Array.isArray(props.onScroll._argMapping)) {
229
- // Because of a react-native issue https://github.com/facebook/react-native/issues/13294
230
- argMapping.pop();
231
- // @ts-expect-error Let's ignore for now that trick
232
- const [argMap] = props.onScroll._argMapping;
233
- if (argMap && argMap.nativeEvent && argMap.nativeEvent.contentOffset) {
234
- // Shares the same animated value passed in props
235
- this._scrollPos =
236
- argMap.nativeEvent.contentOffset.x ||
237
- argMap.nativeEvent.contentOffset.y ||
238
- this._scrollPos;
239
- }
240
- // @ts-expect-error Let's ignore for now that trick
241
- argMapping.push(...props.onScroll._argMapping);
216
+
217
+ _setScrollHandler(props) {
218
+ // Native driver for scroll events
219
+ const scrollEventConfig = {
220
+ listener: this._onScroll,
221
+ useNativeDriver: true,
222
+ };
223
+ this._scrollPos = new Animated.Value(0);
224
+ const argMapping = props.vertical
225
+ ? [{ nativeEvent: { contentOffset: { y: this._scrollPos } } }]
226
+ : [{ nativeEvent: { contentOffset: { x: this._scrollPos } } }];
227
+
228
+ // @ts-expect-error Let's ignore for now that trick
229
+ if (props.onScroll && Array.isArray(props.onScroll._argMapping)) {
230
+ // Because of a react-native issue https://github.com/facebook/react-native/issues/13294
231
+ argMapping.pop();
232
+ // @ts-expect-error Let's ignore for now that trick
233
+ const [argMap] = props.onScroll._argMapping;
234
+ if (
235
+ argMap &&
236
+ argMap.nativeEvent &&
237
+ argMap.nativeEvent.contentOffset
238
+ ) {
239
+ // Shares the same animated value passed in props
240
+ this._scrollPos =
241
+ argMap.nativeEvent.contentOffset.x ||
242
+ argMap.nativeEvent.contentOffset.y ||
243
+ this._scrollPos;
244
+ }
245
+ // @ts-expect-error Let's ignore for now that trick
246
+ argMapping.push(...props.onScroll._argMapping);
247
+ }
248
+ this._onScrollHandler = Animated.event(argMapping, scrollEventConfig);
242
249
  }
243
- this._onScrollHandler = Animated.event(argMapping, scrollEventConfig);
244
- }
245
-
246
- // This will return a future-proof version code number compatible with semantic versioning
247
- // Examples: 0.59.3 -> 5903 / 0.61.4 -> 6104 / 0.62.12 -> 6212 / 1.0.2 -> 10002
248
- // _getRNVersionCode () {
249
- // const version = RN_PACKAGE && RN_PACKAGE.version;
250
- // if (!version) {
251
- // return null;
252
- // }
253
- // const versionSplit = version.split('.');
254
- // if (!versionSplit || !versionSplit.length) {
255
- // return null;
256
- // }
257
- // return versionSplit[0] * 10000 +
258
- // (typeof versionSplit[1] !== 'undefined' ? versionSplit[1] * 100 : 0) +
259
- // (typeof versionSplit[2] !== 'undefined' ? versionSplit[2] * 1 : 0);
260
- // }
261
-
262
- _displayWarnings(props = this.props) {
263
- const pluginName = 'react-native-snap-carousel';
264
- const removedProps = [
265
- 'activeAnimationType',
266
- 'activeAnimationOptions',
267
- 'enableMomentum',
268
- 'lockScrollTimeoutDuration',
269
- 'lockScrollWhileSnapping',
270
- 'onBeforeSnapToItem',
271
- 'swipeThreshold',
272
- ];
273
-
274
- // if (this._RNVersionCode && this._RNVersionCode < 5800) {
275
- // console.error(
276
- // `${pluginName}: Version 4+ of the plugin is based on React Native props that were introduced in version 0.58. ` +
277
- // 'Please downgrade to version 3.x or update your version of React Native.'
278
- // );
250
+
251
+ // This will return a future-proof version code number compatible with semantic versioning
252
+ // Examples: 0.59.3 -> 5903 / 0.61.4 -> 6104 / 0.62.12 -> 6212 / 1.0.2 -> 10002
253
+ // _getRNVersionCode () {
254
+ // const version = RN_PACKAGE && RN_PACKAGE.version;
255
+ // if (!version) {
256
+ // return null;
257
+ // }
258
+ // const versionSplit = version.split('.');
259
+ // if (!versionSplit || !versionSplit.length) {
260
+ // return null;
261
+ // }
262
+ // return versionSplit[0] * 10000 +
263
+ // (typeof versionSplit[1] !== 'undefined' ? versionSplit[1] * 100 : 0) +
264
+ // (typeof versionSplit[2] !== 'undefined' ? versionSplit[2] * 1 : 0);
279
265
  // }
280
- if (!props.vertical && (!props.sliderWidth || !props.itemWidth)) {
281
- console.error(
282
- `${pluginName}: You need to specify both 'sliderWidth' and 'itemWidth' for horizontal carousels`,
283
- );
266
+
267
+ _displayWarnings(props = this.props) {
268
+ const pluginName = 'react-native-snap-carousel';
269
+ const removedProps = [
270
+ 'activeAnimationType',
271
+ 'activeAnimationOptions',
272
+ 'enableMomentum',
273
+ 'lockScrollTimeoutDuration',
274
+ 'lockScrollWhileSnapping',
275
+ 'onBeforeSnapToItem',
276
+ 'swipeThreshold',
277
+ ];
278
+
279
+ // if (this._RNVersionCode && this._RNVersionCode < 5800) {
280
+ // console.error(
281
+ // `${pluginName}: Version 4+ of the plugin is based on React Native props that were introduced in version 0.58. ` +
282
+ // 'Please downgrade to version 3.x or update your version of React Native.'
283
+ // );
284
+ // }
285
+ if (!props.vertical && (!props.sliderWidth || !props.itemWidth)) {
286
+ console.error(
287
+ `${pluginName}: You need to specify both 'sliderWidth' and 'itemWidth' for horizontal carousels`,
288
+ );
289
+ }
290
+ if (props.vertical && (!props.sliderHeight || !props.itemHeight)) {
291
+ console.error(
292
+ `${pluginName}: You need to specify both 'sliderHeight' and 'itemHeight' for vertical carousels`,
293
+ );
294
+ }
295
+
296
+ removedProps.forEach((removedProp) => {
297
+ if (removedProp in props) {
298
+ console.warn(
299
+ `${pluginName}: Prop ${removedProp} has been removed in version 4 of the plugin`,
300
+ );
301
+ }
302
+ });
284
303
  }
285
- if (props.vertical && (!props.sliderHeight || !props.itemHeight)) {
286
- console.error(
287
- `${pluginName}: You need to specify both 'sliderHeight' and 'itemHeight' for vertical carousels`,
288
- );
304
+
305
+ _needsScrollView() {
306
+ const { useScrollView } = this.props;
307
+ // Android's cell renderer is buggy and has a stange overflow
308
+ // TODO: a workaround might be to pass the custom animated styles directly to it
309
+ return IS_ANDROID
310
+ ? useScrollView ||
311
+ !Animated.FlatList ||
312
+ this._shouldUseStackLayout() ||
313
+ this._shouldUseTinderLayout()
314
+ : useScrollView || !Animated.FlatList;
289
315
  }
290
316
 
291
- removedProps.forEach(removedProp => {
292
- if (removedProp in props) {
293
- console.warn(
294
- `${pluginName}: Prop ${removedProp} has been removed in version 4 of the plugin`,
295
- );
296
- }
297
- });
298
- }
299
-
300
- _needsScrollView() {
301
- const {useScrollView} = this.props;
302
- // Android's cell renderer is buggy and has a stange overflow
303
- // TODO: a workaround might be to pass the custom animated styles directly to it
304
- return IS_ANDROID
305
- ? useScrollView ||
306
- !Animated.FlatList ||
307
- this._shouldUseStackLayout() ||
308
- this._shouldUseTinderLayout()
309
- : useScrollView || !Animated.FlatList;
310
- }
311
-
312
- _needsRTLAdaptations() {
313
- const {vertical} = this.props;
314
- return IS_RTL && IS_ANDROID && !vertical;
315
- }
316
-
317
- _enableLoop() {
318
- const {data, enableSnap, loop} = this.props;
319
- return enableSnap && loop && data && data.length && data.length > 1;
320
- }
321
-
322
- _shouldAnimateSlides(props = this.props) {
323
- const {
324
- inactiveSlideOpacity,
325
- inactiveSlideScale,
326
- scrollInterpolator,
327
- slideInterpolatedStyle,
328
- } = props;
329
- return (
330
- inactiveSlideOpacity < 1 ||
331
- inactiveSlideScale < 1 ||
332
- !!scrollInterpolator ||
333
- !!slideInterpolatedStyle ||
334
- this._shouldUseShiftLayout() ||
335
- this._shouldUseStackLayout() ||
336
- this._shouldUseTinderLayout()
337
- );
338
- }
339
-
340
- _shouldUseShiftLayout() {
341
- const {inactiveSlideShift, layout} = this.props;
342
- return layout === 'default' && inactiveSlideShift !== 0;
343
- }
344
-
345
- _shouldUseStackLayout() {
346
- return this.props.layout === 'stack';
347
- }
348
-
349
- _shouldUseTinderLayout() {
350
- return this.props.layout === 'tinder';
351
- }
352
-
353
- _shouldRepositionScroll(index) {
354
- const {data, enableSnap, loopClonesPerSide} = this.props;
355
- const dataLength = data && data.length;
356
- if (
357
- !enableSnap ||
358
- !dataLength ||
359
- !this._enableLoop() ||
360
- (index >= loopClonesPerSide && index < dataLength + loopClonesPerSide)
361
- ) {
362
- return false;
317
+ _needsRTLAdaptations() {
318
+ const { vertical } = this.props;
319
+ return IS_RTL && IS_ANDROID && !vertical;
363
320
  }
364
- return true;
365
- }
366
321
 
367
- _isMultiple(x, y) {
368
- // This prevents Javascript precision issues: https://stackoverflow.com/a/58440614/
369
- // Required because Android viewport size can return pretty complicated decimals numbers
370
- return Math.round(Math.round(x / y) / (1 / y)) === Math.round(x);
371
- }
322
+ _enableLoop() {
323
+ const { data, enableSnap, loop } = this.props;
324
+ return enableSnap && loop && data && data.length && data.length > 1;
325
+ }
372
326
 
373
- _getCustomData(props = this.props) {
374
- const {data, loopClonesPerSide} = props;
375
- const dataLength = data && data.length;
327
+ _shouldAnimateSlides(props = this.props) {
328
+ const {
329
+ inactiveSlideOpacity,
330
+ inactiveSlideScale,
331
+ scrollInterpolator,
332
+ slideInterpolatedStyle,
333
+ } = props;
334
+ return (
335
+ inactiveSlideOpacity < 1 ||
336
+ inactiveSlideScale < 1 ||
337
+ !!scrollInterpolator ||
338
+ !!slideInterpolatedStyle ||
339
+ this._shouldUseShiftLayout() ||
340
+ this._shouldUseStackLayout() ||
341
+ this._shouldUseTinderLayout()
342
+ );
343
+ }
376
344
 
377
- if (!dataLength) {
378
- return [];
345
+ _shouldUseShiftLayout() {
346
+ const { inactiveSlideShift, layout } = this.props;
347
+ return layout === 'default' && inactiveSlideShift !== 0;
379
348
  }
380
349
 
381
- if (!this._enableLoop()) {
382
- return data;
350
+ _shouldUseStackLayout() {
351
+ return this.props.layout === 'stack';
383
352
  }
384
353
 
385
- let previousItems = [];
386
- let nextItems = [];
354
+ _shouldUseTinderLayout() {
355
+ return this.props.layout === 'tinder';
356
+ }
387
357
 
388
- if (loopClonesPerSide > dataLength) {
389
- const dataMultiplier = Math.floor(loopClonesPerSide / dataLength);
390
- const remainder = loopClonesPerSide % dataLength;
358
+ _shouldRepositionScroll(index) {
359
+ const { data, enableSnap, loopClonesPerSide } = this.props;
360
+ const dataLength = data && data.length;
361
+ if (
362
+ !enableSnap ||
363
+ !dataLength ||
364
+ !this._enableLoop() ||
365
+ (index >= loopClonesPerSide &&
366
+ index < dataLength + loopClonesPerSide)
367
+ ) {
368
+ return false;
369
+ }
370
+ return true;
371
+ }
391
372
 
392
- for (let i = 0; i < dataMultiplier; i++) {
393
- previousItems.push(...data);
394
- nextItems.push(...data);
395
- }
373
+ _roundNumber(num, decimals = 1) {
374
+ // https://stackoverflow.com/a/41716722/
375
+ const rounder = Math.pow(10, decimals);
376
+ return Math.round((num + Number.EPSILON) * rounder) / rounder;
377
+ }
396
378
 
397
- previousItems.unshift(...data.slice(-remainder));
398
- nextItems.push(...data.slice(0, remainder));
399
- } else {
400
- previousItems = data.slice(-loopClonesPerSide);
401
- nextItems = data.slice(0, loopClonesPerSide);
379
+ _isMultiple(x, y) {
380
+ // This prevents Javascript precision issues: https://stackoverflow.com/a/58440614/
381
+ // Required because Android viewport size can return pretty complicated decimals numbers
382
+ return Math.round(Math.round(x / y) / (1 / y)) === Math.round(x);
402
383
  }
403
384
 
404
- return previousItems.concat(data, nextItems);
405
- }
385
+ _getCustomData(props = this.props) {
386
+ const { data, loopClonesPerSide } = props;
387
+ const dataLength = data && data.length;
388
+
389
+ if (!dataLength) {
390
+ return [];
391
+ }
392
+
393
+ if (!this._enableLoop()) {
394
+ return data;
395
+ }
396
+
397
+ let previousItems = [];
398
+ let nextItems = [];
406
399
 
407
- _getCustomDataLength(props = this.props) {
408
- const {data, loopClonesPerSide} = props;
409
- const dataLength = data && data.length;
400
+ if (loopClonesPerSide > dataLength) {
401
+ const dataMultiplier = Math.floor(loopClonesPerSide / dataLength);
402
+ const remainder = loopClonesPerSide % dataLength;
410
403
 
411
- if (!dataLength) {
412
- return 0;
404
+ for (let i = 0; i < dataMultiplier; i++) {
405
+ previousItems.push(...data);
406
+ nextItems.push(...data);
407
+ }
408
+
409
+ previousItems.unshift(...data.slice(-remainder));
410
+ nextItems.push(...data.slice(0, remainder));
411
+ } else {
412
+ previousItems = data.slice(-loopClonesPerSide);
413
+ nextItems = data.slice(0, loopClonesPerSide);
414
+ }
415
+
416
+ return previousItems.concat(data, nextItems);
413
417
  }
414
418
 
415
- return this._enableLoop() ? dataLength + 2 * loopClonesPerSide : dataLength;
416
- }
419
+ _getCustomDataLength(props = this.props) {
420
+ const { data, loopClonesPerSide } = props;
421
+ const dataLength = data && data.length;
417
422
 
418
- _getCustomIndex(index, props = this.props) {
419
- const itemsLength = this._getCustomDataLength(props);
423
+ if (!dataLength) {
424
+ return 0;
425
+ }
420
426
 
421
- if (!itemsLength || typeof index === 'undefined') {
422
- return 0;
427
+ return this._enableLoop()
428
+ ? dataLength + 2 * loopClonesPerSide
429
+ : dataLength;
423
430
  }
424
431
 
425
- return this._needsRTLAdaptations() ? itemsLength - index - 1 : index;
426
- }
432
+ _getCustomIndex(index, props = this.props) {
433
+ const itemsLength = this._getCustomDataLength(props);
427
434
 
428
- _getDataIndex(index) {
429
- const {data, loopClonesPerSide} = this.props;
430
- const dataLength = data && data.length;
431
- if (!this._enableLoop() || !dataLength) {
432
- return index;
435
+ if (!itemsLength || typeof index === 'undefined') {
436
+ return 0;
437
+ }
438
+
439
+ return this._needsRTLAdaptations() ? itemsLength - index - 1 : index;
433
440
  }
434
441
 
435
- if (index >= dataLength + loopClonesPerSide) {
436
- return loopClonesPerSide > dataLength
437
- ? (index - loopClonesPerSide) % dataLength
438
- : index - dataLength - loopClonesPerSide;
439
- } else if (index < loopClonesPerSide) {
440
- // TODO: is there a simpler way of determining the interpolated index?
441
- if (loopClonesPerSide > dataLength) {
442
- const baseDataIndexes = [];
443
- const dataIndexes = [];
444
- const dataMultiplier = Math.floor(loopClonesPerSide / dataLength);
445
- const remainder = loopClonesPerSide % dataLength;
446
-
447
- for (let i = 0; i < dataLength; i++) {
448
- baseDataIndexes.push(i);
442
+ _getDataIndex(index) {
443
+ const { data, loopClonesPerSide } = this.props;
444
+ const dataLength = data && data.length;
445
+ if (!this._enableLoop() || !dataLength) {
446
+ return index;
449
447
  }
450
448
 
451
- for (let j = 0; j < dataMultiplier; j++) {
452
- dataIndexes.push(...baseDataIndexes);
449
+ if (index >= dataLength + loopClonesPerSide) {
450
+ return loopClonesPerSide > dataLength
451
+ ? (index - loopClonesPerSide) % dataLength
452
+ : index - dataLength - loopClonesPerSide;
453
+ } else if (index < loopClonesPerSide) {
454
+ // TODO: is there a simpler way of determining the interpolated index?
455
+ if (loopClonesPerSide > dataLength) {
456
+ const baseDataIndexes = [];
457
+ const dataIndexes = [];
458
+ const dataMultiplier = Math.floor(
459
+ loopClonesPerSide / dataLength,
460
+ );
461
+ const remainder = loopClonesPerSide % dataLength;
462
+
463
+ for (let i = 0; i < dataLength; i++) {
464
+ baseDataIndexes.push(i);
465
+ }
466
+
467
+ for (let j = 0; j < dataMultiplier; j++) {
468
+ dataIndexes.push(...baseDataIndexes);
469
+ }
470
+
471
+ dataIndexes.unshift(...baseDataIndexes.slice(-remainder));
472
+ return dataIndexes[index];
473
+ } else {
474
+ return index + dataLength - loopClonesPerSide;
475
+ }
476
+ } else {
477
+ return index - loopClonesPerSide;
453
478
  }
479
+ }
454
480
 
455
- dataIndexes.unshift(...baseDataIndexes.slice(-remainder));
456
- return dataIndexes[index];
457
- } else {
458
- return index + dataLength - loopClonesPerSide;
459
- }
460
- } else {
461
- return index - loopClonesPerSide;
481
+ // Used with `snapToItem()` and 'PaginationDot'
482
+ _getPositionIndex(index) {
483
+ const { loop, loopClonesPerSide } = this.props;
484
+ return loop ? index + loopClonesPerSide : index;
462
485
  }
463
- }
464
-
465
- // Used with `snapToItem()` and 'PaginationDot'
466
- _getPositionIndex(index) {
467
- const {loop, loopClonesPerSide} = this.props;
468
- return loop ? index + loopClonesPerSide : index;
469
- }
470
-
471
- _getSnapOffsets(props = this.props) {
472
- const offset = this._getItemMainDimension();
473
- if (!props.enableSnap) return;
474
- return [...Array(this._getCustomDataLength(props))].map((_, i) => {
475
- return i * offset;
476
- });
477
- }
478
-
479
- _getFirstItem(index, props = this.props) {
480
- const {loopClonesPerSide} = props;
481
- const itemsLength = this._getCustomDataLength(props);
482
-
483
- if (!itemsLength || index > itemsLength - 1 || index < 0) {
484
- return 0;
486
+
487
+ _getSnapOffsets(props = this.props) {
488
+ const offset = this._getItemMainDimension();
489
+ if (!props.enableSnap) return;
490
+ return [...Array(this._getCustomDataLength(props))].map((_, i) => {
491
+ return i * offset;
492
+ });
485
493
  }
486
494
 
487
- return this._enableLoop() ? index + loopClonesPerSide : index;
488
- }
495
+ _getFirstItem(index, props = this.props) {
496
+ const { loopClonesPerSide } = props;
497
+ const itemsLength = this._getCustomDataLength(props);
489
498
 
490
- _getWrappedRef() {
491
- // Starting with RN 0.62, we should no longer call `getNode()` on the ref of an Animated component
492
- if (
493
- this._carouselRef &&
494
- ((this._needsScrollView() && this._carouselRef.scrollTo) ||
495
- (!this._needsScrollView() && this._carouselRef.scrollToOffset))
496
- ) {
497
- return this._carouselRef;
498
- }
499
- // https://github.com/facebook/react-native/issues/10635
500
- // https://stackoverflow.com/a/48786374/8412141
501
- return (
502
- this._carouselRef &&
503
- // @ts-expect-error This is for before 0.62
504
- this._carouselRef.getNode &&
505
- // @ts-expect-error This is for before 0.62
506
- this._carouselRef.getNode()
507
- );
508
- }
509
-
510
- _getScrollEnabled() {
511
- return this._scrollEnabled;
512
- }
513
-
514
- _setScrollEnabled(scrollEnabled = true) {
515
- const wrappedRef = this._getWrappedRef();
516
-
517
- if (!wrappedRef || !wrappedRef.setNativeProps) {
518
- return;
499
+ if (!itemsLength || index > itemsLength - 1 || index < 0) {
500
+ return 0;
501
+ }
502
+
503
+ return this._enableLoop() ? index + loopClonesPerSide : index;
519
504
  }
520
505
 
521
- // 'setNativeProps()' is used instead of 'setState()' because the latter
522
- // really takes a toll on Android behavior when momentum is disabled
523
- wrappedRef.setNativeProps({scrollEnabled});
524
- this._scrollEnabled = scrollEnabled;
525
- }
526
-
527
- _getItemMainDimension() {
528
- return this.props.vertical ? this.props.itemHeight : this.props.itemWidth;
529
- }
530
-
531
- _getItemScrollOffset(index) {
532
- return (
533
- this._positions && this._positions[index] && this._positions[index].start
534
- );
535
- }
536
-
537
- _getItemLayout(_, index) {
538
- const itemMainDimension = this._getItemMainDimension();
539
- return {
540
- index,
541
- length: itemMainDimension,
542
- offset: itemMainDimension * index, // + this._getContainerInnerMargin()
543
- };
544
- }
545
-
546
- // This will allow us to have a proper zIndex even with a FlatList
547
- // https://github.com/facebook/react-native/issues/18616#issuecomment-389444165
548
- _getCellRendererComponent({children, index, style, ...props}) {
549
- const cellStyle = [
550
- style,
551
- !IS_ANDROID ? {zIndex: this._getCustomDataLength() - index} : {},
552
- ];
553
-
554
- return (
555
- <View style={cellStyle} key={index} {...props}>
556
- {children}
557
- </View>
558
- );
559
- }
560
-
561
- _getKeyExtractor(_, index) {
562
- return this._needsScrollView()
563
- ? `scrollview-item-${index}`
564
- : `flatlist-item-${index}`;
565
- }
566
-
567
- _getScrollOffset(event) {
568
- const {vertical} = this.props;
569
- return (
570
- (event &&
571
- event.nativeEvent &&
572
- event.nativeEvent.contentOffset &&
573
- event.nativeEvent.contentOffset[vertical ? 'y' : 'x']) ||
574
- 0
575
- );
576
- }
577
-
578
- _getContainerInnerMargin(opposite = false) {
579
- const {activeSlideAlignment} = this.props;
580
-
581
- if (
582
- (activeSlideAlignment === 'start' && !opposite) ||
583
- (activeSlideAlignment === 'end' && opposite)
584
- ) {
585
- return 0;
586
- } else if (
587
- (activeSlideAlignment === 'end' && !opposite) ||
588
- (activeSlideAlignment === 'start' && opposite)
589
- ) {
590
- return this.props.vertical
591
- ? this.props.sliderHeight - this.props.itemHeight
592
- : this.props.sliderWidth - this.props.itemWidth;
593
- } else {
594
- return this.props.vertical
595
- ? (this.props.sliderHeight - this.props.itemHeight) / 2
596
- : (this.props.sliderWidth - this.props.itemWidth) / 2;
506
+ _getWrappedRef() {
507
+ // Starting with RN 0.62, we should no longer call `getNode()` on the ref of an Animated component
508
+ if (
509
+ this._carouselRef &&
510
+ ((this._needsScrollView() && this._carouselRef.scrollTo) ||
511
+ (!this._needsScrollView() && this._carouselRef.scrollToOffset))
512
+ ) {
513
+ return this._carouselRef;
514
+ }
515
+ // https://github.com/facebook/react-native/issues/10635
516
+ // https://stackoverflow.com/a/48786374/8412141
517
+ return (
518
+ this._carouselRef &&
519
+ // @ts-expect-error This is for before 0.62
520
+ this._carouselRef.getNode &&
521
+ // @ts-expect-error This is for before 0.62
522
+ this._carouselRef.getNode()
523
+ );
597
524
  }
598
- }
599
-
600
- _getActiveSlideOffset() {
601
- const {activeSlideOffset} = this.props;
602
- const itemMainDimension = this._getItemMainDimension();
603
- const minOffset = 10;
604
- // Make sure activeSlideOffset never prevents the active area from being at least 10 px wide
605
- return itemMainDimension / 2 - activeSlideOffset >= minOffset
606
- ? activeSlideOffset
607
- : minOffset;
608
- }
609
-
610
- _getActiveItem(offset) {
611
- const itemMainDimension = this._getItemMainDimension();
612
- const center = offset + itemMainDimension / 2;
613
- const activeSlideOffset = this._getActiveSlideOffset();
614
- const lastIndex = this._positions.length - 1;
615
- let itemIndex;
616
-
617
- if (offset <= 0) {
618
- return 0;
525
+
526
+ _getScrollEnabled() {
527
+ return this._scrollEnabled;
619
528
  }
620
529
 
621
- if (
622
- this._positions[lastIndex] &&
623
- offset >= this._positions[lastIndex].start
624
- ) {
625
- return lastIndex;
530
+ _setScrollEnabled(scrollEnabled = true) {
531
+ const wrappedRef = this._getWrappedRef();
532
+
533
+ if (!wrappedRef || !wrappedRef.setNativeProps) {
534
+ return;
535
+ }
536
+
537
+ // 'setNativeProps()' is used instead of 'setState()' because the latter
538
+ // really takes a toll on Android behavior when momentum is disabled
539
+ wrappedRef.setNativeProps({ scrollEnabled });
540
+ this._scrollEnabled = scrollEnabled;
626
541
  }
627
542
 
628
- for (let i = 0; i < this._positions.length; i++) {
629
- const {start, end} = this._positions[i];
630
- if (
631
- center + activeSlideOffset >= start &&
632
- center - activeSlideOffset <= end
633
- ) {
634
- itemIndex = i;
635
- break;
636
- }
543
+ _getItemMainDimension() {
544
+ return this.props.vertical
545
+ ? this.props.itemHeight
546
+ : this.props.itemWidth;
637
547
  }
638
548
 
639
- return itemIndex || 0;
640
- }
549
+ _getItemScrollOffset(index) {
550
+ return (
551
+ this._positions &&
552
+ this._positions[index] &&
553
+ this._positions[index].start
554
+ );
555
+ }
641
556
 
642
- _getSlideInterpolatedStyle(index, animatedValue) {
643
- const {layoutCardOffset, slideInterpolatedStyle} = this.props;
557
+ _getItemLayout(_, index) {
558
+ const itemMainDimension = this._getItemMainDimension();
559
+ return {
560
+ index,
561
+ length: itemMainDimension,
562
+ offset: itemMainDimension * index, // + this._getContainerInnerMargin()
563
+ };
564
+ }
644
565
 
645
- if (slideInterpolatedStyle) {
646
- return slideInterpolatedStyle(index, animatedValue, this.props);
647
- } else if (this._shouldUseTinderLayout()) {
648
- return tinderAnimatedStyles(
649
- index,
650
- animatedValue,
651
- this.props,
652
- layoutCardOffset,
653
- );
654
- } else if (this._shouldUseStackLayout()) {
655
- return stackAnimatedStyles(
656
- index,
657
- animatedValue,
658
- this.props,
659
- layoutCardOffset,
660
- );
661
- } else if (this._shouldUseShiftLayout()) {
662
- return shiftAnimatedStyles(index, animatedValue, this.props);
663
- } else {
664
- return defaultAnimatedStyles(index, animatedValue, this.props);
566
+ // This will allow us to have a proper zIndex even with a FlatList
567
+ // https://github.com/facebook/react-native/issues/18616#issuecomment-389444165
568
+ _getCellRendererComponent({ children, index, style, ...props }) {
569
+ const cellStyle = [
570
+ style,
571
+ !IS_ANDROID ? { zIndex: this._getCustomDataLength() - index } : {},
572
+ ];
573
+
574
+ return (
575
+ <View style={cellStyle} key={index} {...props}>
576
+ {children}
577
+ </View>
578
+ );
665
579
  }
666
- }
667
580
 
668
- _initPositionsAndInterpolators(props = this.props) {
669
- const {data, scrollInterpolator} = props;
670
- const itemMainDimension = this._getItemMainDimension();
581
+ _getKeyExtractor(_, index) {
582
+ return this._needsScrollView()
583
+ ? `scrollview-item-${index}`
584
+ : `flatlist-item-${index}`;
585
+ }
671
586
 
672
- if (!data || !data.length) {
673
- return;
587
+ _getScrollOffset(event) {
588
+ const { vertical } = this.props;
589
+ return (
590
+ (event &&
591
+ event.nativeEvent &&
592
+ event.nativeEvent.contentOffset &&
593
+ event.nativeEvent.contentOffset[vertical ? 'y' : 'x']) ||
594
+ 0
595
+ );
674
596
  }
675
597
 
676
- const interpolators = [];
677
- this._positions = [];
598
+ _getContainerInnerMargin(opposite = false) {
599
+ const { activeSlideAlignment } = this.props;
678
600
 
679
- this._getCustomData(props).forEach((_itemData, index) => {
680
- const _index = this._getCustomIndex(index, props);
681
- let animatedValue;
601
+ if (
602
+ (activeSlideAlignment === 'start' && !opposite) ||
603
+ (activeSlideAlignment === 'end' && opposite)
604
+ ) {
605
+ return 0;
606
+ } else if (
607
+ (activeSlideAlignment === 'end' && !opposite) ||
608
+ (activeSlideAlignment === 'start' && opposite)
609
+ ) {
610
+ return this.props.vertical
611
+ ? this.props.sliderHeight - this.props.itemHeight
612
+ : this.props.sliderWidth - this.props.itemWidth;
613
+ } else {
614
+ return this.props.vertical
615
+ ? (this.props.sliderHeight - this.props.itemHeight) / 2
616
+ : (this.props.sliderWidth - this.props.itemWidth) / 2;
617
+ }
618
+ }
682
619
 
683
- this._positions[index] = {
684
- start: index * itemMainDimension,
685
- end: index * itemMainDimension + itemMainDimension,
686
- };
620
+ _getActiveSlideOffset() {
621
+ const { activeSlideOffset } = this.props;
622
+ const itemMainDimension = this._getItemMainDimension();
623
+ const minOffset = 10;
624
+ // Make sure activeSlideOffset never prevents the active area from being at least 10 px wide
625
+ return itemMainDimension / 2 - activeSlideOffset >= minOffset
626
+ ? activeSlideOffset
627
+ : minOffset;
628
+ }
687
629
 
688
- if (!this._shouldAnimateSlides(props) || !this._scrollPos) {
689
- animatedValue = new Animated.Value(1);
690
- } else {
691
- let interpolator;
630
+ _getActiveItem(offset) {
631
+ const itemMainDimension = this._getItemMainDimension();
632
+ const center = offset + itemMainDimension / 2;
633
+ const activeSlideOffset = this._getActiveSlideOffset();
634
+ const lastIndex = this._positions.length - 1;
635
+ let itemIndex;
692
636
 
693
- if (scrollInterpolator) {
694
- interpolator = scrollInterpolator(_index, props);
695
- } else if (this._shouldUseStackLayout()) {
696
- interpolator = stackScrollInterpolator(_index, props);
697
- } else if (this._shouldUseTinderLayout()) {
698
- interpolator = tinderScrollInterpolator(_index, props);
637
+ if (offset <= 0) {
638
+ return 0;
699
639
  }
700
640
 
701
641
  if (
702
- !interpolator ||
703
- !interpolator.inputRange ||
704
- !interpolator.outputRange
642
+ this._positions[lastIndex] &&
643
+ offset >= this._positions[lastIndex].start
705
644
  ) {
706
- interpolator = defaultScrollInterpolator(_index, props);
645
+ return lastIndex;
707
646
  }
708
647
 
709
- animatedValue = this._scrollPos.interpolate({
710
- ...interpolator,
711
- extrapolate: 'clamp',
712
- });
713
- }
714
-
715
- interpolators.push(animatedValue);
716
- });
648
+ for (let i = 0; i < this._positions.length; i++) {
649
+ const { start, end } = this._positions[i];
650
+ if (
651
+ center + activeSlideOffset >= start &&
652
+ center - activeSlideOffset <= end
653
+ ) {
654
+ itemIndex = i;
655
+ break;
656
+ }
657
+ }
717
658
 
718
- this.setState({interpolators});
719
- }
659
+ return itemIndex || 0;
660
+ }
720
661
 
721
- _hackActiveSlideAnimation(index, scrollValue = 1) {
722
- const offset = this._getItemScrollOffset(index);
662
+ _getSlideInterpolatedStyle(index, animatedValue) {
663
+ const { layoutCardOffset, slideInterpolatedStyle } = this.props;
723
664
 
724
- if (!this._mounted || !this._carouselRef || typeof offset === 'undefined') {
725
- return;
665
+ if (slideInterpolatedStyle) {
666
+ return slideInterpolatedStyle(index, animatedValue, this.props);
667
+ } else if (this._shouldUseTinderLayout()) {
668
+ return tinderAnimatedStyles(
669
+ index,
670
+ animatedValue,
671
+ this.props,
672
+ layoutCardOffset,
673
+ );
674
+ } else if (this._shouldUseStackLayout()) {
675
+ return stackAnimatedStyles(
676
+ index,
677
+ animatedValue,
678
+ this.props,
679
+ layoutCardOffset,
680
+ );
681
+ } else if (this._shouldUseShiftLayout()) {
682
+ return shiftAnimatedStyles(index, animatedValue, this.props);
683
+ } else {
684
+ return defaultAnimatedStyles(index, animatedValue, this.props);
685
+ }
726
686
  }
727
687
 
728
- const multiplier = this._currentScrollOffset === 0 ? 1 : -1;
729
- const scrollDelta = scrollValue * multiplier;
730
-
731
- this._scrollTo({
732
- offset: offset + scrollDelta,
733
- animated: false,
734
- });
735
-
736
- // @ts-expect-error setTimeout / clearTiemout is buggy :/
737
- clearTimeout(this._hackSlideAnimationTimeout);
738
- this._hackSlideAnimationTimeout = setTimeout(() => {
739
- this._scrollTo({
740
- offset,
741
- animated: false,
742
- });
743
- }, 1); // works randomly when set to '0'
744
- }
745
-
746
- _repositionScroll(index, animated = false) {
747
- const {data, loopClonesPerSide} = this.props;
748
- const dataLength = data && data.length;
749
-
750
- if (typeof index === 'undefined' || !this._shouldRepositionScroll(index)) {
751
- return;
752
- }
688
+ _initPositionsAndInterpolators(props = this.props) {
689
+ const { data, scrollInterpolator } = props;
690
+ const itemMainDimension = this._getItemMainDimension();
691
+
692
+ if (!data || !data.length) {
693
+ return;
694
+ }
753
695
 
754
- let repositionTo = index;
696
+ const interpolators = [];
697
+ this._positions = [];
698
+
699
+ this._getCustomData(props).forEach((_itemData, index) => {
700
+ const _index = this._getCustomIndex(index, props);
701
+ let animatedValue;
702
+
703
+ this._positions[index] = {
704
+ start: index * itemMainDimension,
705
+ end: index * itemMainDimension + itemMainDimension,
706
+ };
707
+
708
+ if (!this._shouldAnimateSlides(props) || !this._scrollPos) {
709
+ animatedValue = new Animated.Value(1);
710
+ } else {
711
+ let interpolator;
712
+
713
+ if (scrollInterpolator) {
714
+ interpolator = scrollInterpolator(_index, props);
715
+ } else if (this._shouldUseStackLayout()) {
716
+ interpolator = stackScrollInterpolator(_index, props);
717
+ } else if (this._shouldUseTinderLayout()) {
718
+ interpolator = tinderScrollInterpolator(_index, props);
719
+ }
720
+
721
+ if (
722
+ !interpolator ||
723
+ !interpolator.inputRange ||
724
+ !interpolator.outputRange
725
+ ) {
726
+ interpolator = defaultScrollInterpolator(_index, props);
727
+ }
728
+
729
+ animatedValue = this._scrollPos.interpolate({
730
+ ...interpolator,
731
+ extrapolate: 'clamp',
732
+ });
733
+ }
734
+
735
+ interpolators.push(animatedValue);
736
+ });
755
737
 
756
- if (index >= dataLength + loopClonesPerSide) {
757
- repositionTo = index - dataLength;
758
- } else if (index < loopClonesPerSide) {
759
- repositionTo = index + dataLength;
738
+ this.setState({ interpolators });
760
739
  }
761
740
 
762
- this._snapToItem(repositionTo, animated, false);
763
- }
741
+ _hackActiveSlideAnimation(index, scrollValue = 1) {
742
+ const offset = this._getItemScrollOffset(index);
764
743
 
765
- _scrollTo({offset, index, animated = true}) {
766
- const {vertical} = this.props;
767
- const wrappedRef = this._getWrappedRef();
768
- if (
769
- !this._mounted ||
770
- !wrappedRef ||
771
- (typeof offset === 'undefined' && typeof index === 'undefined')
772
- ) {
773
- return;
744
+ if (
745
+ !this._mounted ||
746
+ !this._carouselRef ||
747
+ typeof offset === 'undefined'
748
+ ) {
749
+ return;
750
+ }
751
+
752
+ const multiplier = this._currentScrollOffset === 0 ? 1 : -1;
753
+ const scrollDelta = scrollValue * multiplier;
754
+
755
+ this._scrollTo({ offset: offset + scrollDelta, animated: false });
756
+
757
+ // @ts-expect-error setTimeout / clearTiemout is buggy :/
758
+ clearTimeout(this._hackSlideAnimationTimeout);
759
+ this._hackSlideAnimationTimeout = setTimeout(() => {
760
+ this._scrollTo({ offset, animated: false });
761
+ }, 1); // works randomly when set to '0'
774
762
  }
775
763
 
776
- let scrollToOffset;
777
- if (typeof index !== 'undefined') {
778
- scrollToOffset = this._getItemScrollOffset(index);
779
- } else {
780
- scrollToOffset = offset;
764
+ _repositionScroll(index, animated = false) {
765
+ const { data, loopClonesPerSide } = this.props;
766
+ const dataLength = data && data.length;
767
+
768
+ if (
769
+ typeof index === 'undefined' ||
770
+ !this._shouldRepositionScroll(index)
771
+ ) {
772
+ return;
773
+ }
774
+
775
+ let repositionTo = index;
776
+
777
+ if (index >= dataLength + loopClonesPerSide) {
778
+ repositionTo = index - dataLength;
779
+ } else if (index < loopClonesPerSide) {
780
+ repositionTo = index + dataLength;
781
+ }
782
+
783
+ this._snapToItem(repositionTo, animated, false);
781
784
  }
782
785
 
783
- if (typeof scrollToOffset === 'undefined') {
784
- return;
786
+ _scrollTo({ offset, index, animated = true }) {
787
+ const { vertical } = this.props;
788
+ const wrappedRef = this._getWrappedRef();
789
+ if (
790
+ !this._mounted ||
791
+ !wrappedRef ||
792
+ (typeof offset === 'undefined' && typeof index === 'undefined')
793
+ ) {
794
+ return;
795
+ }
796
+
797
+ let scrollToOffset;
798
+ if (typeof index !== 'undefined') {
799
+ scrollToOffset = this._getItemScrollOffset(index);
800
+ } else {
801
+ scrollToOffset = offset;
802
+ }
803
+
804
+ if (typeof scrollToOffset === 'undefined') {
805
+ return;
806
+ }
807
+
808
+ const options = this._needsScrollView()
809
+ ? {
810
+ x: vertical ? 0 : offset,
811
+ y: vertical ? offset : 0,
812
+ animated,
813
+ }
814
+ : {
815
+ offset,
816
+ animated,
817
+ };
818
+
819
+ if (this._needsScrollView()) {
820
+ wrappedRef.scrollTo(options);
821
+ } else {
822
+ wrappedRef.scrollToOffset(options);
823
+ }
785
824
  }
786
825
 
787
- const options = this._needsScrollView()
788
- ? {
789
- x: vertical ? 0 : offset,
790
- y: vertical ? offset : 0,
791
- animated,
826
+ _onTouchStart(event) {
827
+ const { onTouchStart } = this.props;
828
+
829
+ // `onTouchStart` is fired even when `scrollEnabled` is set to `false`
830
+ if (this._getScrollEnabled() !== false && this._autoplaying) {
831
+ this.pauseAutoPlay();
792
832
  }
793
- : {
794
- offset,
795
- animated,
796
- };
797
833
 
798
- if (this._needsScrollView()) {
799
- wrappedRef.scrollTo(options);
800
- } else {
801
- wrappedRef.scrollToOffset(options);
834
+ onTouchStart && onTouchStart(event);
802
835
  }
803
- }
804
836
 
805
- _onTouchStart(event) {
806
- const {onTouchStart} = this.props;
837
+ _onTouchEnd(event) {
838
+ const { onTouchEnd } = this.props;
807
839
 
808
- // `onTouchStart` is fired even when `scrollEnabled` is set to `false`
809
- if (this._getScrollEnabled() !== false && this._autoplaying) {
810
- this.pauseAutoPlay();
840
+ if (
841
+ this._getScrollEnabled() !== false &&
842
+ this._autoplay &&
843
+ !this._autoplaying
844
+ ) {
845
+ // This event is buggy on Android, so a fallback is provided in _onMomentumScrollEnd()
846
+ this.startAutoplay();
847
+ }
848
+
849
+ onTouchEnd && onTouchEnd(event);
811
850
  }
812
851
 
813
- onTouchStart && onTouchStart(event);
814
- }
852
+ _onScroll(event) {
853
+ const { onScroll, onScrollIndexChanged } = this.props;
854
+ const scrollOffset = event
855
+ ? this._getScrollOffset(event)
856
+ : this._currentScrollOffset;
857
+ const nextActiveItem = this._getActiveItem(scrollOffset);
858
+ const dataLength = this._getCustomDataLength();
859
+ const lastItemScrollOffset = this._getItemScrollOffset(dataLength - 1);
860
+
861
+ this._currentScrollOffset = scrollOffset;
862
+
863
+ if (nextActiveItem !== this._onScrollActiveItem) {
864
+ this._onScrollActiveItem = nextActiveItem;
865
+ onScrollIndexChanged &&
866
+ onScrollIndexChanged(this._getDataIndex(nextActiveItem));
867
+ }
815
868
 
816
- _onTouchEnd(event) {
817
- const {onTouchEnd} = this.props;
869
+ if (
870
+ (IS_IOS && scrollOffset >= lastItemScrollOffset) ||
871
+ (IS_ANDROID &&
872
+ Math.floor(scrollOffset) >= Math.floor(lastItemScrollOffset))
873
+ ) {
874
+ this._activeItem = nextActiveItem;
875
+ this._repositionScroll(nextActiveItem);
876
+ }
818
877
 
819
- if (
820
- this._getScrollEnabled() !== false &&
821
- this._autoplay &&
822
- !this._autoplaying
823
- ) {
824
- // This event is buggy on Android, so a fallback is provided in _onMomentumScrollEnd()
825
- this.startAutoplay();
878
+ if (typeof onScroll === 'function' && event) {
879
+ onScroll(event);
880
+ }
826
881
  }
827
882
 
828
- onTouchEnd && onTouchEnd(event);
829
- }
883
+ _onMomentumScrollEnd(event) {
884
+ const { autoplayDelay, onMomentumScrollEnd, onSnapToItem } = this.props;
885
+ const scrollOffset = event
886
+ ? this._getScrollOffset(event)
887
+ : this._currentScrollOffset;
888
+ const nextActiveItem = this._getActiveItem(scrollOffset);
889
+ const hasSnapped = this._isMultiple(
890
+ scrollOffset,
891
+ this.props.vertical ? this.props.itemHeight : this.props.itemWidth,
892
+ );
830
893
 
831
- _onScroll(event) {
832
- const {onScroll, onScrollIndexChanged, onSnapToItem} = this.props;
833
- const scrollOffset = event
834
- ? this._getScrollOffset(event)
835
- : this._currentScrollOffset;
836
- const nextActiveItem = this._getActiveItem(scrollOffset);
837
- const dataLength = this._getCustomDataLength();
838
- const lastItemScrollOffset = this._getItemScrollOffset(dataLength - 1);
894
+ // WARNING: everything in this condition will probably need to be called on _snapToItem as well because:
895
+ // 1. `onMomentumScrollEnd` won't be called if the scroll isn't animated
896
+ // 2. `onMomentumScrollEnd` won't be called at all on Android when scrolling programmatically
897
+ if (nextActiveItem !== this._activeItem) {
898
+ this._activeItem = nextActiveItem;
899
+ onSnapToItem && onSnapToItem(this._getDataIndex(nextActiveItem));
900
+
901
+ if (hasSnapped && IS_ANDROID) {
902
+ this._repositionScroll(nextActiveItem);
903
+ } else if (IS_IOS) {
904
+ this._repositionScroll(nextActiveItem);
905
+ }
906
+ }
839
907
 
840
- this._currentScrollOffset = scrollOffset;
908
+ onMomentumScrollEnd && onMomentumScrollEnd(event);
841
909
 
842
- if (nextActiveItem !== this._onScrollActiveItem) {
843
- this._onScrollActiveItem = nextActiveItem;
844
- onScrollIndexChanged &&
845
- onScrollIndexChanged(this._getDataIndex(nextActiveItem));
910
+ // The touchEnd event is buggy on Android, so this will serve as a fallback whenever needed
911
+ // https://github.com/facebook/react-native/issues/9439
912
+ if (IS_ANDROID && this._autoplay && !this._autoplaying) {
913
+ // @ts-expect-error setTimeout / clearTiemout is buggy :/
914
+ clearTimeout(this._enableAutoplayTimeout);
915
+ this._enableAutoplayTimeout = setTimeout(() => {
916
+ this.startAutoplay();
917
+ }, autoplayDelay);
918
+ }
919
+ }
920
+
921
+ _onLayout(event) {
922
+ const { onLayout } = this.props;
923
+
924
+ // Prevent unneeded actions during the first 'onLayout' (triggered on init)
925
+ if (this._onLayoutInitDone) {
926
+ this._initPositionsAndInterpolators();
927
+ this._snapToItem(this._activeItem, false, false, true);
928
+ } else {
929
+ this._onLayoutInitDone = true;
930
+ }
846
931
 
847
- onSnapToItem && onSnapToItem(this._getDataIndex(nextActiveItem));
932
+ onLayout && onLayout(event);
848
933
  }
849
934
 
850
- //last item
851
- if (
852
- (IS_IOS && scrollOffset > lastItemScrollOffset) ||
853
- (IS_ANDROID &&
854
- Math.floor(scrollOffset) > Math.floor(lastItemScrollOffset))
935
+ _snapToItem(
936
+ index,
937
+ animated = true,
938
+ fireCallback = true,
939
+ forceScrollTo = false,
855
940
  ) {
856
- this._activeItem = nextActiveItem;
857
- this._repositionScroll(nextActiveItem);
858
- }
941
+ const { onSnapToItem } = this.props;
942
+ const itemsLength = this._getCustomDataLength();
943
+ const wrappedRef = this._getWrappedRef();
944
+ if (!itemsLength || !wrappedRef) {
945
+ return;
946
+ }
859
947
 
860
- if (typeof onScroll === 'function' && event) {
861
- onScroll(event);
862
- }
863
- }
864
-
865
- _onMomentumScrollEnd(event) {
866
- const {autoplayDelay, onMomentumScrollEnd, onSnapToItem} = this.props;
867
- const scrollOffset = event
868
- ? this._getScrollOffset(event)
869
- : this._currentScrollOffset;
870
- const nextActiveItem = this._getActiveItem(scrollOffset);
871
- const hasSnapped = this._isMultiple(
872
- scrollOffset,
873
- this.props.vertical ? this.props.itemHeight : this.props.itemWidth,
874
- );
875
-
876
- // WARNING: everything in this condition will probably need to be called on _snapToItem as well because:
877
- // 1. `onMomentumScrollEnd` won't be called if the scroll isn't animated
878
- // 2. `onMomentumScrollEnd` won't be called at all on Android when scrolling programmatically
879
- if (nextActiveItem !== this._activeItem) {
880
- this._activeItem = nextActiveItem;
881
- onSnapToItem && onSnapToItem(this._getDataIndex(nextActiveItem));
882
-
883
- if (hasSnapped && IS_ANDROID) {
884
- this._repositionScroll(nextActiveItem);
885
- } else if (IS_IOS) {
886
- this._repositionScroll(nextActiveItem);
887
- }
888
- }
948
+ if (!index || index < 0) {
949
+ index = 0;
950
+ } else if (itemsLength > 0 && index >= itemsLength) {
951
+ index = itemsLength - 1;
952
+ }
889
953
 
890
- onMomentumScrollEnd && onMomentumScrollEnd(event);
954
+ if (index === this._activeItem && !forceScrollTo) {
955
+ return;
956
+ }
891
957
 
892
- // The touchEnd event is buggy on Android, so this will serve as a fallback whenever needed
893
- // https://github.com/facebook/react-native/issues/9439
894
- if (IS_ANDROID && this._autoplay && !this._autoplaying) {
895
- // @ts-expect-error setTimeout / clearTiemout is buggy :/
896
- clearTimeout(this._enableAutoplayTimeout);
897
- this._enableAutoplayTimeout = setTimeout(() => {
898
- this.startAutoplay();
899
- }, autoplayDelay);
900
- }
901
- }
958
+ const offset = this._getItemScrollOffset(index);
902
959
 
903
- _onLayout(event) {
904
- const {onLayout} = this.props;
960
+ if (offset === undefined) {
961
+ return;
962
+ }
905
963
 
906
- // Prevent unneeded actions during the first 'onLayout' (triggered on init)
907
- if (this._onLayoutInitDone) {
908
- this._initPositionsAndInterpolators();
909
- this._snapToItem(this._activeItem, false, false, true);
910
- } else {
911
- this._onLayoutInitDone = true;
964
+ this._scrollTo({ offset, animated });
965
+
966
+ // On both platforms, `onMomentumScrollEnd` won't be triggered if the scroll isn't animated
967
+ // so we need to trigger the callback manually
968
+ // On Android `onMomentumScrollEnd` won't be triggered when scrolling programmatically
969
+ // Therefore everything critical needs to be manually called here as well, even though the timing might be off
970
+ const requiresManualTrigger = !animated || IS_ANDROID;
971
+ if (requiresManualTrigger) {
972
+ this._activeItem = index;
973
+
974
+ if (fireCallback) {
975
+ onSnapToItem && onSnapToItem(this._getDataIndex(index));
976
+ }
977
+
978
+ // Repositioning on Android
979
+ if (IS_ANDROID && this._shouldRepositionScroll(index)) {
980
+ if (animated) {
981
+ this._androidRepositioningTimeout = setTimeout(() => {
982
+ // Without scroll animation, the behavior is completely buggy...
983
+ this._repositionScroll(index, false);
984
+ }, 400); // Approximate scroll duration on Android
985
+ } else {
986
+ this._repositionScroll(index);
987
+ }
988
+ }
989
+ }
912
990
  }
913
991
 
914
- onLayout && onLayout(event);
915
- }
916
-
917
- _snapToItem(
918
- index,
919
- animated = true,
920
- fireCallback = true,
921
- forceScrollTo = false,
922
- ) {
923
- const {onSnapToItem} = this.props;
924
- const itemsLength = this._getCustomDataLength();
925
- const wrappedRef = this._getWrappedRef();
926
- if (!itemsLength || !wrappedRef) {
927
- return;
992
+ startAutoplay() {
993
+ const { autoplayInterval, autoplayDelay } = this.props;
994
+ this._autoplay = true;
995
+
996
+ if (this._autoplaying) {
997
+ return;
998
+ }
999
+
1000
+ // @ts-expect-error setTimeout / clearTiemout is buggy :/
1001
+ clearTimeout(this._autoplayTimeout);
1002
+ this._autoplayTimeout = setTimeout(() => {
1003
+ this._autoplaying = true;
1004
+ this._autoplayInterval = setInterval(() => {
1005
+ if (this._autoplaying) {
1006
+ this.snapToNext();
1007
+ }
1008
+ }, autoplayInterval);
1009
+ }, autoplayDelay);
928
1010
  }
929
1011
 
930
- if (!index || index < 0) {
931
- index = 0;
932
- } else if (itemsLength > 0 && index >= itemsLength) {
933
- index = itemsLength - 1;
1012
+ pauseAutoPlay() {
1013
+ this._autoplaying = false;
1014
+ // @ts-expect-error setTimeout / clearTiemout is buggy :/
1015
+ clearTimeout(this._autoplayTimeout);
1016
+ // @ts-expect-error setTimeout / clearTiemout is buggy :/
1017
+ clearTimeout(this._enableAutoplayTimeout);
1018
+ // @ts-expect-error setTimeout / clearTiemout is buggy :/
1019
+ clearInterval(this._autoplayInterval);
934
1020
  }
935
1021
 
936
- if (index === this._activeItem && !forceScrollTo) {
937
- return;
1022
+ stopAutoplay() {
1023
+ this._autoplay = false;
1024
+ this.pauseAutoPlay();
938
1025
  }
939
1026
 
940
- const offset = this._getItemScrollOffset(index);
1027
+ snapToItem(index, animated = true, fireCallback = true) {
1028
+ if (!index || index < 0) {
1029
+ index = 0;
1030
+ }
941
1031
 
942
- if (offset === undefined) {
943
- return;
944
- }
1032
+ const positionIndex = this._getPositionIndex(index);
945
1033
 
946
- this._scrollTo({
947
- offset,
948
- animated,
949
- });
950
-
951
- // On both platforms, `onMomentumScrollEnd` won't be triggered if the scroll isn't animated
952
- // so we need to trigger the callback manually
953
- // On Android `onMomentumScrollEnd` won't be triggered when scrolling programmatically
954
- // Therefore everything critical needs to be manually called here as well, even though the timing might be off
955
- const requiresManualTrigger = !animated || IS_ANDROID;
956
- if (requiresManualTrigger) {
957
- this._activeItem = index;
958
-
959
- if (fireCallback) {
960
- onSnapToItem && onSnapToItem(this._getDataIndex(index));
961
- }
962
-
963
- // Repositioning on Android
964
- if (IS_ANDROID && this._shouldRepositionScroll(index)) {
965
- if (animated) {
966
- this._androidRepositioningTimeout = setTimeout(() => {
967
- // Without scroll animation, the behavior is completely buggy...
968
- this._repositionScroll(index, false);
969
- }, 400); // Approximate scroll duration on Android
970
- } else {
971
- this._repositionScroll(index);
1034
+ if (positionIndex === this._activeItem) {
1035
+ return;
972
1036
  }
973
- }
1037
+
1038
+ this._snapToItem(positionIndex, animated, fireCallback);
974
1039
  }
975
- }
976
1040
 
977
- startAutoplay() {
978
- const {autoplayInterval, autoplayDelay} = this.props;
979
- this._autoplay = true;
1041
+ snapToNext(animated = true, fireCallback = true) {
1042
+ const itemsLength = this._getCustomDataLength();
980
1043
 
981
- if (this._autoplaying) {
982
- return;
1044
+ let newIndex = this._activeItem + 1;
1045
+ if (newIndex > itemsLength - 1) {
1046
+ newIndex = 0;
1047
+ }
1048
+ this._snapToItem(newIndex, animated, fireCallback);
983
1049
  }
984
1050
 
985
- // @ts-expect-error setTimeout / clearTiemout is buggy :/
986
- clearTimeout(this._autoplayTimeout);
987
- this._autoplayTimeout = setTimeout(() => {
988
- this._autoplaying = true;
989
- this._autoplayInterval = setInterval(() => {
990
- if (this._autoplaying) {
991
- this.snapToNext();
1051
+ snapToPrev(animated = true, fireCallback = true) {
1052
+ const itemsLength = this._getCustomDataLength();
1053
+
1054
+ let newIndex = this._activeItem - 1;
1055
+ if (newIndex < 0) {
1056
+ newIndex = itemsLength - 1;
992
1057
  }
993
- }, autoplayInterval);
994
- }, autoplayDelay);
995
- }
996
-
997
- pauseAutoPlay() {
998
- this._autoplaying = false;
999
- // @ts-expect-error setTimeout / clearTiemout is buggy :/
1000
- clearTimeout(this._autoplayTimeout);
1001
- // @ts-expect-error setTimeout / clearTiemout is buggy :/
1002
- clearTimeout(this._enableAutoplayTimeout);
1003
- // @ts-expect-error setTimeout / clearTiemout is buggy :/
1004
- clearInterval(this._autoplayInterval);
1005
- }
1006
-
1007
- stopAutoplay() {
1008
- this._autoplay = false;
1009
- this.pauseAutoPlay();
1010
- }
1011
-
1012
- snapToNext(animated = true, fireCallback = true) {
1013
- const itemsLength = this._getCustomDataLength();
1014
-
1015
- let newIndex = this._activeItem + 1;
1016
- if (newIndex > itemsLength - 1) {
1017
- newIndex = 0;
1058
+ this._snapToItem(newIndex, animated, fireCallback);
1018
1059
  }
1019
- this._snapToItem(newIndex, animated, fireCallback);
1020
- }
1021
-
1022
- snapToPrev(animated = true, fireCallback = true) {
1023
- const itemsLength = this._getCustomDataLength();
1024
1060
 
1025
- let newIndex = this._activeItem - 1;
1026
- if (newIndex < 0) {
1027
- newIndex = itemsLength - 1;
1061
+ // https://github.com/facebook/react-native/issues/1831#issuecomment-231069668
1062
+ triggerRenderingHack(offset = 1) {
1063
+ this._hackActiveSlideAnimation(this._activeItem, offset);
1028
1064
  }
1029
- this._snapToItem(newIndex, animated, fireCallback);
1030
- }
1031
1065
 
1032
- // https://github.com/facebook/react-native/issues/1831#issuecomment-231069668
1033
- triggerRenderingHack(offset = 1) {
1034
- this._hackActiveSlideAnimation(this._activeItem, offset);
1035
- }
1066
+ _renderItem({ item, index }) {
1067
+ const { interpolators } = this.state;
1068
+ const { keyExtractor, slideStyle } = this.props;
1069
+ const animatedValue = interpolators && interpolators[index];
1036
1070
 
1037
- _renderItem({item, index}) {
1038
- const {interpolators} = this.state;
1039
- const {keyExtractor, slideStyle} = this.props;
1040
- const animatedValue = interpolators && interpolators[index];
1071
+ if (typeof animatedValue === 'undefined') {
1072
+ return null;
1073
+ }
1041
1074
 
1042
- if (typeof animatedValue === 'undefined') {
1043
- return null;
1075
+ const animate = this._shouldAnimateSlides();
1076
+ const Component = animate ? Animated.View : View;
1077
+ const animatedStyle = animate
1078
+ ? this._getSlideInterpolatedStyle(index, animatedValue)
1079
+ : {};
1080
+ const dataIndex = this._getDataIndex(index);
1081
+
1082
+ const mainDimension = this.props.vertical
1083
+ ? { height: this.props.itemHeight }
1084
+ : { width: this.props.itemWidth };
1085
+ const specificProps = this._needsScrollView()
1086
+ ? {
1087
+ key: keyExtractor
1088
+ ? keyExtractor(item, index)
1089
+ : this._getKeyExtractor(item, index),
1090
+ }
1091
+ : {};
1092
+
1093
+ return (
1094
+ <Component
1095
+ style={[mainDimension, slideStyle, animatedStyle]}
1096
+ pointerEvents="box-none"
1097
+ {...specificProps}>
1098
+ {this.props.vertical
1099
+ ? this.props.renderItem(
1100
+ {
1101
+ item,
1102
+ index,
1103
+ dataIndex,
1104
+ realIndex: this._getDataIndex(index),
1105
+ activeIndex: this._getDataIndex(this._activeItem),
1106
+ },
1107
+ {
1108
+ scrollPosition: this._scrollPos,
1109
+ carouselRef: this._carouselRef,
1110
+ vertical: this.props.vertical,
1111
+ sliderHeight: this.props.sliderHeight,
1112
+ itemHeight: this.props.itemHeight,
1113
+ },
1114
+ )
1115
+ : this.props.renderItem(
1116
+ {
1117
+ item,
1118
+ index,
1119
+ dataIndex,
1120
+ realIndex: this._getDataIndex(index),
1121
+ activeIndex: this._getDataIndex(this._activeItem),
1122
+ },
1123
+ {
1124
+ scrollPosition: this._scrollPos,
1125
+ carouselRef: this._carouselRef,
1126
+ vertical: !!this.props.vertical,
1127
+ sliderWidth: this.props.sliderWidth,
1128
+ itemWidth: this.props.itemWidth,
1129
+ },
1130
+ )}
1131
+ </Component>
1132
+ );
1044
1133
  }
1045
1134
 
1046
- const animate = this._shouldAnimateSlides();
1047
- const Component = animate ? Animated.View : View;
1048
- const animatedStyle = animate
1049
- ? this._getSlideInterpolatedStyle(index, animatedValue)
1050
- : {};
1051
- const dataIndex = this._getDataIndex(index);
1052
-
1053
- const mainDimension = this.props.vertical
1054
- ? {height: this.props.itemHeight}
1055
- : {width: this.props.itemWidth};
1056
- const specificProps = this._needsScrollView()
1057
- ? {
1058
- key: keyExtractor
1059
- ? keyExtractor(item, index)
1060
- : this._getKeyExtractor(item, index),
1061
- }
1062
- : {};
1063
-
1064
- return (
1065
- <Component
1066
- style={[mainDimension, slideStyle, animatedStyle]}
1067
- pointerEvents="box-none"
1068
- {...specificProps}>
1069
- {this.props.vertical
1070
- ? this.props.renderItem(
1071
- {
1072
- item,
1073
- index,
1074
- dataIndex,
1075
- realIndex: this._getDataIndex(index),
1076
- activeIndex: this._getDataIndex(this._activeItem),
1077
- },
1078
- {
1079
- scrollPosition: this._scrollPos,
1080
- carouselRef: this._carouselRef,
1081
- vertical: this.props.vertical,
1082
- sliderHeight: this.props.sliderHeight,
1083
- itemHeight: this.props.itemHeight,
1084
- },
1085
- )
1086
- : this.props.renderItem(
1087
- {
1088
- item,
1089
- index,
1090
- dataIndex,
1091
- realIndex: this._getDataIndex(index),
1092
- activeIndex: this._getDataIndex(this._activeItem),
1093
- },
1094
- {
1095
- scrollPosition: this._scrollPos,
1096
- carouselRef: this._carouselRef,
1097
- vertical: !!this.props.vertical,
1098
- sliderWidth: this.props.sliderWidth,
1099
- itemWidth: this.props.itemWidth,
1100
- },
1101
- )}
1102
- </Component>
1103
- );
1104
- }
1105
-
1106
- _getComponentOverridableProps() {
1107
- const {hideCarousel} = this.state;
1108
- const {loopClonesPerSide} = this.props;
1109
- const visibleItems =
1110
- Math.ceil(
1111
- this.props.vertical
1112
- ? this.props.sliderHeight / this.props.itemHeight
1113
- : this.props.sliderWidth / this.props.itemWidth,
1114
- ) + 1;
1115
- const initialNumPerSide = this._enableLoop() ? loopClonesPerSide : 2;
1116
- const initialNumToRender =
1117
- visibleItems > 2
1118
- ? visibleItems + initialNumPerSide * 2
1119
- : initialNumPerSide * 2;
1120
- const maxToRenderPerBatch = initialNumToRender;
1121
- const windowSize = maxToRenderPerBatch;
1122
- const specificProps = !this._needsScrollView()
1123
- ? {
1124
- initialNumToRender,
1125
- maxToRenderPerBatch,
1126
- windowSize,
1127
- // updateCellsBatchingPeriod
1128
- }
1129
- : {};
1130
-
1131
- return {
1132
- ...specificProps,
1133
- automaticallyAdjustContentInsets: false,
1134
- decelerationRate: 'fast',
1135
- directionalLockEnabled: true,
1136
- disableScrollViewPanResponder: false, // If set to `true`, touch events will be triggered too easily
1137
- inverted: this._needsRTLAdaptations(),
1138
- overScrollMode: 'never',
1139
- pinchGestureEnabled: false,
1140
- pointerEvents: hideCarousel ? 'none' : 'auto',
1141
- // removeClippedSubviews: !this._needsScrollView(),
1142
- // renderToHardwareTextureAndroid: true,
1143
- scrollsToTop: false,
1144
- showsHorizontalScrollIndicator: false,
1145
- showsVerticalScrollIndicator: false,
1146
- };
1147
- }
1148
-
1149
- _getComponentStaticProps() {
1150
- const {hideCarousel} = this.state;
1151
- const {
1152
- activeSlideAlignment,
1153
- CellRendererComponent,
1154
- containerCustomStyle,
1155
- contentContainerCustomStyle,
1156
- firstItem,
1157
- getItemLayout,
1158
- keyExtractor,
1159
- style,
1160
- useExperimentalSnap,
1161
- disableIntervalMomentum,
1162
- vertical,
1163
- enableSnap,
1164
- } = this.props;
1165
- const containerStyle = [
1166
- // { overflow: 'hidden' },
1167
- containerCustomStyle || style || {},
1168
- hideCarousel ? {opacity: 0} : {},
1169
- this.props.vertical
1170
- ? {
1171
- height: this.props.sliderHeight,
1172
- flexDirection: 'column',
1173
- } // LTR hack; see https://github.com/facebook/react-native/issues/11960
1174
- : // and https://github.com/facebook/react-native/issues/13100#issuecomment-328986423
1175
- {
1176
- width: this.props.sliderWidth,
1177
- flexDirection: this._needsRTLAdaptations() ? 'row-reverse' : 'row',
1178
- },
1179
- ];
1180
-
1181
- const innerMarginStyle = this.props.vertical
1182
- ? {
1183
- paddingTop: this._getContainerInnerMargin(),
1184
- paddingBottom: this._getContainerInnerMargin(true),
1185
- }
1186
- : {
1187
- paddingLeft: this._getContainerInnerMargin(),
1188
- paddingRight: this._getContainerInnerMargin(true),
1135
+ _getComponentOverridableProps() {
1136
+ const { hideCarousel } = this.state;
1137
+ const { loopClonesPerSide } = this.props;
1138
+ const visibleItems =
1139
+ Math.ceil(
1140
+ this.props.vertical
1141
+ ? this.props.sliderHeight / this.props.itemHeight
1142
+ : this.props.sliderWidth / this.props.itemWidth,
1143
+ ) + 1;
1144
+ const initialNumPerSide = this._enableLoop() ? loopClonesPerSide : 2;
1145
+ const initialNumToRender =
1146
+ visibleItems > 2
1147
+ ? visibleItems + initialNumPerSide * 2
1148
+ : initialNumPerSide * 2;
1149
+ const maxToRenderPerBatch = initialNumToRender;
1150
+ const windowSize = maxToRenderPerBatch;
1151
+ const specificProps = !this._needsScrollView()
1152
+ ? {
1153
+ initialNumToRender,
1154
+ maxToRenderPerBatch,
1155
+ windowSize,
1156
+ // updateCellsBatchingPeriod
1157
+ }
1158
+ : {};
1159
+
1160
+ return {
1161
+ ...specificProps,
1162
+ automaticallyAdjustContentInsets: false,
1163
+ decelerationRate: 'fast',
1164
+ directionalLockEnabled: true,
1165
+ disableScrollViewPanResponder: false, // If set to `true`, touch events will be triggered too easily
1166
+ inverted: this._needsRTLAdaptations(),
1167
+ overScrollMode: 'never',
1168
+ pinchGestureEnabled: false,
1169
+ pointerEvents: hideCarousel ? 'none' : 'auto',
1170
+ // removeClippedSubviews: !this._needsScrollView(),
1171
+ // renderToHardwareTextureAndroid: true,
1172
+ scrollsToTop: false,
1173
+ showsHorizontalScrollIndicator: false,
1174
+ showsVerticalScrollIndicator: false,
1189
1175
  };
1176
+ }
1190
1177
 
1191
- const contentContainerStyle = [
1192
- vertical
1193
- ? {
1194
- paddingTop: this._getContainerInnerMargin(),
1195
- paddingBottom: this._getContainerInnerMargin(true),
1196
- }
1197
- : {
1198
- paddingLeft: this._getContainerInnerMargin(),
1199
- paddingRight: this._getContainerInnerMargin(true),
1200
- },
1201
- contentContainerCustomStyle || {},
1202
- ];
1203
-
1204
- // WARNING: `snapToAlignment` won't work as intended because of the following:
1205
- // https://github.com/facebook/react-native/blob/d0871d0a9a373e1d3ac35da46c85c0d0e793116d/React/Views/ScrollView/RCTScrollView.m#L751-L755
1206
- // - Snap points will be off
1207
- // - Slide animations will be off
1208
- // - Last items won't be set as active (no `onSnapToItem` callback)
1209
- // Recommended only with large slides and `activeSlideAlignment` set to `start` for the time being
1210
- const snapProps =
1211
- enableSnap && useExperimentalSnap
1212
- ? {
1213
- disableIntervalMomentum, // Slide ± one item at a time
1214
- snapToAlignment: activeSlideAlignment,
1215
- snapToInterval: this._getItemMainDimension(),
1216
- }
1217
- : {
1218
- snapToOffsets: this._getSnapOffsets(),
1219
- };
1220
-
1221
- // Flatlist specifics
1222
- const specificProps = !this._needsScrollView()
1223
- ? {
1224
- CellRendererComponent:
1225
- CellRendererComponent || this._getCellRendererComponent,
1226
- getItemLayout: getItemLayout || this._getItemLayout,
1227
- initialScrollIndex: this._getFirstItem(firstItem),
1228
- keyExtractor: keyExtractor || this._getKeyExtractor,
1229
- numColumns: 1,
1230
- renderItem: this._renderItem,
1231
- }
1232
- : {};
1233
-
1234
- return {
1235
- ...specificProps,
1236
- ...snapProps,
1237
- // eslint-disable-next-line @typescript-eslint/no-explicit-any
1238
- ref: c => {
1239
- this._carouselRef = c;
1240
- },
1241
- contentContainerStyle: contentContainerStyle,
1242
- data: this._getCustomData(),
1243
- horizontal: !this.props.vertical,
1244
- scrollEventThrottle: 1,
1245
- style: containerStyle,
1246
- onLayout: this._onLayout,
1247
- onMomentumScrollEnd: this._onMomentumScrollEnd,
1248
- onScroll: this._onScrollHandler,
1249
- onTouchStart: this._onTouchStart,
1250
- onTouchEnd: this._onTouchEnd,
1251
- };
1252
- }
1253
-
1254
- render() {
1255
- const {data, renderItem, useScrollView} = this.props;
1256
-
1257
- if (!data || !renderItem) {
1258
- return null;
1178
+ _getComponentStaticProps() {
1179
+ const { hideCarousel } = this.state;
1180
+ const {
1181
+ activeSlideAlignment,
1182
+ CellRendererComponent,
1183
+ containerCustomStyle,
1184
+ contentContainerCustomStyle,
1185
+ firstItem,
1186
+ getItemLayout,
1187
+ keyExtractor,
1188
+ style,
1189
+ useExperimentalSnap,
1190
+ disableIntervalMomentum,
1191
+ vertical,
1192
+ enableSnap,
1193
+ } = this.props;
1194
+ const containerStyle = [
1195
+ // { overflow: 'hidden' },
1196
+ containerCustomStyle || style || {},
1197
+ hideCarousel ? { opacity: 0 } : {},
1198
+ this.props.vertical
1199
+ ? { height: this.props.sliderHeight, flexDirection: 'column' } // LTR hack; see https://github.com/facebook/react-native/issues/11960
1200
+ : // and https://github.com/facebook/react-native/issues/13100#issuecomment-328986423
1201
+ {
1202
+ width: this.props.sliderWidth,
1203
+ flexDirection: this._needsRTLAdaptations()
1204
+ ? 'row-reverse'
1205
+ : 'row',
1206
+ },
1207
+ ];
1208
+
1209
+ const innerMarginStyle = this.props.vertical
1210
+ ? {
1211
+ paddingTop: this._getContainerInnerMargin(),
1212
+ paddingBottom: this._getContainerInnerMargin(true),
1213
+ }
1214
+ : {
1215
+ paddingLeft: this._getContainerInnerMargin(),
1216
+ paddingRight: this._getContainerInnerMargin(true),
1217
+ };
1218
+
1219
+ const contentContainerStyle = [
1220
+ vertical
1221
+ ? {
1222
+ paddingTop: this._getContainerInnerMargin(),
1223
+ paddingBottom: this._getContainerInnerMargin(true),
1224
+ }
1225
+ : {
1226
+ paddingLeft: this._getContainerInnerMargin(),
1227
+ paddingRight: this._getContainerInnerMargin(true),
1228
+ },
1229
+ contentContainerCustomStyle || {},
1230
+ ];
1231
+
1232
+ // WARNING: `snapToAlignment` won't work as intended because of the following:
1233
+ // https://github.com/facebook/react-native/blob/d0871d0a9a373e1d3ac35da46c85c0d0e793116d/React/Views/ScrollView/RCTScrollView.m#L751-L755
1234
+ // - Snap points will be off
1235
+ // - Slide animations will be off
1236
+ // - Last items won't be set as active (no `onSnapToItem` callback)
1237
+ // Recommended only with large slides and `activeSlideAlignment` set to `start` for the time being
1238
+ const snapProps =
1239
+ enableSnap && useExperimentalSnap
1240
+ ? {
1241
+ disableIntervalMomentum, // Slide ± one item at a time
1242
+ snapToAlignment: activeSlideAlignment,
1243
+ snapToInterval: this._getItemMainDimension(),
1244
+ }
1245
+ : {
1246
+ snapToOffsets: this._getSnapOffsets(),
1247
+ };
1248
+
1249
+ // Flatlist specifics
1250
+ const specificProps = !this._needsScrollView()
1251
+ ? {
1252
+ CellRendererComponent:
1253
+ CellRendererComponent || this._getCellRendererComponent,
1254
+ getItemLayout: getItemLayout || this._getItemLayout,
1255
+ initialScrollIndex: this._getFirstItem(firstItem),
1256
+ keyExtractor: keyExtractor || this._getKeyExtractor,
1257
+ numColumns: 1,
1258
+ renderItem: this._renderItem,
1259
+ }
1260
+ : {};
1261
+
1262
+ return {
1263
+ ...specificProps,
1264
+ ...snapProps,
1265
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
1266
+ ref: (c) => {
1267
+ this._carouselRef = c;
1268
+ },
1269
+ contentContainerStyle: contentContainerStyle,
1270
+ data: this._getCustomData(),
1271
+ horizontal: !this.props.vertical,
1272
+ scrollEventThrottle: 1,
1273
+ style: containerStyle,
1274
+ onLayout: this._onLayout,
1275
+ onMomentumScrollEnd: this._onMomentumScrollEnd,
1276
+ onScroll: this._onScrollHandler,
1277
+ onTouchStart: this._onTouchStart,
1278
+ onTouchEnd: this._onTouchEnd,
1279
+ };
1259
1280
  }
1260
1281
 
1261
- const props = {
1262
- ...this._getComponentOverridableProps(),
1263
- ...this.props,
1264
- ...this._getComponentStaticProps(),
1265
- };
1266
-
1267
- const ScrollViewComponent =
1268
- typeof useScrollView === 'function' ? useScrollView : Animated.ScrollView;
1269
- return this._needsScrollView() || !Animated.FlatList ? (
1270
- <ScrollViewComponent {...props}>
1271
- {this._getCustomData().map((item, index) => {
1272
- return this._renderItem({
1273
- item,
1274
- index,
1275
- realIndex: this._getDataIndex(index),
1276
- activeIndex: this._getDataIndex(this._activeItem),
1277
- });
1278
- })}
1279
- </ScrollViewComponent>
1280
- ) : (
1281
- // @ts-expect-error Seems complicated to make TS 100% happy, while sharing that many things between
1282
- // flatlist && scrollview implementation. I'll prob try to rewrite parts of the logic to overcome that.
1283
- <Animated.FlatList {...props} />
1284
- );
1285
- }
1282
+ render() {
1283
+ const { data, renderItem, useScrollView } = this.props;
1284
+
1285
+ if (!data || !renderItem) {
1286
+ return null;
1287
+ }
1288
+
1289
+ const props = {
1290
+ ...this._getComponentOverridableProps(),
1291
+ ...this.props,
1292
+ ...this._getComponentStaticProps(),
1293
+ };
1294
+
1295
+ const ScrollViewComponent =
1296
+ typeof useScrollView === 'function'
1297
+ ? useScrollView
1298
+ : Animated.ScrollView;
1299
+ return this._needsScrollView() || !Animated.FlatList ? (
1300
+ <ScrollViewComponent {...props}>
1301
+ {this._getCustomData().map((item, index) => {
1302
+ return this._renderItem({
1303
+ item,
1304
+ index,
1305
+ realIndex: this._getDataIndex(index),
1306
+ activeIndex: this._getDataIndex(this._activeItem),
1307
+ });
1308
+ })}
1309
+ </ScrollViewComponent>
1310
+ ) : (
1311
+ // @ts-expect-error Seems complicated to make TS 100% happy, while sharing that many things between
1312
+ // flatlist && scrollview implementation. I'll prob try to rewrite parts of the logic to overcome that.
1313
+ <Animated.FlatList {...props} />
1314
+ );
1315
+ }
1286
1316
  }
1287
1317
 
1288
1318
  Carousel.propTypes = {
1289
- data: PropTypes.array.isRequired,
1290
- renderItem: PropTypes.func.isRequired,
1291
- itemWidth: PropTypes.number, // required for horizontal carousel
1292
- itemHeight: PropTypes.number, // required for vertical carousel
1293
- sliderWidth: PropTypes.number, // required for horizontal carousel
1294
- sliderHeight: PropTypes.number, // required for vertical carousel
1295
- activeSlideAlignment: PropTypes.oneOf(['center', 'end', 'start']),
1296
- activeSlideOffset: PropTypes.number,
1297
- apparitionDelay: PropTypes.number,
1298
- autoplay: PropTypes.bool,
1299
- autoplayDelay: PropTypes.number,
1300
- autoplayInterval: PropTypes.number,
1301
- callbackOffsetMargin: PropTypes.number,
1302
- containerCustomStyle: PropTypes.oneOfType([
1303
- PropTypes.object,
1304
- PropTypes.array,
1305
- ]),
1306
- contentContainerCustomStyle: PropTypes.oneOfType([
1307
- PropTypes.object,
1308
- PropTypes.array,
1309
- ]),
1310
- enableSnap: PropTypes.bool,
1311
- firstItem: PropTypes.number,
1312
- hasParallaxImages: PropTypes.bool,
1313
- inactiveSlideOpacity: PropTypes.number,
1314
- inactiveSlideScale: PropTypes.number,
1315
- inactiveSlideShift: PropTypes.number,
1316
- layout: PropTypes.oneOf(['default', 'stack', 'tinder']),
1317
- layoutCardOffset: PropTypes.number,
1318
- loop: PropTypes.bool,
1319
- loopClonesPerSide: PropTypes.number,
1320
- scrollEnabled: PropTypes.bool,
1321
- scrollInterpolator: PropTypes.func,
1322
- slideInterpolatedStyle: PropTypes.func,
1323
- slideStyle: PropTypes.object,
1324
- shouldOptimizeUpdates: PropTypes.bool,
1325
- swipeThreshold: PropTypes.number,
1326
- useScrollView: PropTypes.oneOfType([PropTypes.bool, PropTypes.func]),
1327
- vertical: PropTypes.bool,
1328
- showsPagination: PropTypes.bool,
1329
- isCustomScrollWidth: PropTypes.bool,
1330
- disableIntervalMomentum: PropTypes.bool,
1331
- useExperimentalSnap: PropTypes.bool,
1332
- onBeforeSnapToItem: PropTypes.func,
1333
- onSnapToItem: PropTypes.func,
1319
+ data: PropTypes.array.isRequired,
1320
+ renderItem: PropTypes.func.isRequired,
1321
+ itemWidth: PropTypes.number, // required for horizontal carousel
1322
+ itemHeight: PropTypes.number, // required for vertical carousel
1323
+ sliderWidth: PropTypes.number, // required for horizontal carousel
1324
+ sliderHeight: PropTypes.number, // required for vertical carousel
1325
+ activeSlideAlignment: PropTypes.oneOf(['center', 'end', 'start']),
1326
+ activeSlideOffset: PropTypes.number,
1327
+ apparitionDelay: PropTypes.number,
1328
+ autoplay: PropTypes.bool,
1329
+ autoplayDelay: PropTypes.number,
1330
+ autoplayInterval: PropTypes.number,
1331
+ callbackOffsetMargin: PropTypes.number,
1332
+ containerCustomStyle: PropTypes.oneOfType([
1333
+ PropTypes.object,
1334
+ PropTypes.array,
1335
+ ]),
1336
+ contentContainerCustomStyle: PropTypes.oneOfType([
1337
+ PropTypes.object,
1338
+ PropTypes.array,
1339
+ ]),
1340
+ enableSnap: PropTypes.bool,
1341
+ firstItem: PropTypes.number,
1342
+ hasParallaxImages: PropTypes.bool,
1343
+ inactiveSlideOpacity: PropTypes.number,
1344
+ inactiveSlideScale: PropTypes.number,
1345
+ inactiveSlideShift: PropTypes.number,
1346
+ layout: PropTypes.oneOf(['default', 'stack', 'tinder']),
1347
+ layoutCardOffset: PropTypes.number,
1348
+ loop: PropTypes.bool,
1349
+ loopClonesPerSide: PropTypes.number,
1350
+ scrollEnabled: PropTypes.bool,
1351
+ scrollInterpolator: PropTypes.func,
1352
+ slideInterpolatedStyle: PropTypes.func,
1353
+ slideStyle: PropTypes.object,
1354
+ shouldOptimizeUpdates: PropTypes.bool,
1355
+ swipeThreshold: PropTypes.number,
1356
+ useScrollView: PropTypes.oneOfType([PropTypes.bool, PropTypes.func]),
1357
+ vertical: PropTypes.bool,
1358
+ showsPagination: PropTypes.bool,
1359
+ isCustomScrollWidth: PropTypes.bool,
1360
+ disableIntervalMomentum: PropTypes.bool,
1361
+ useExperimentalSnap: PropTypes.bool,
1362
+ onBeforeSnapToItem: PropTypes.func,
1363
+ onSnapToItem: PropTypes.func,
1334
1364
  };
1335
1365
 
1336
1366
  Carousel.defaultProps = {
1337
- activeSlideAlignment: 'center',
1338
- activeSlideOffset: 20,
1339
- apparitionDelay: 0,
1340
- autoplay: false,
1341
- autoplayDelay: 1000,
1342
- autoplayInterval: 3000,
1343
- callbackOffsetMargin: 5,
1344
- containerCustomStyle: {},
1345
- contentContainerCustomStyle: {},
1346
- enableSnap: true,
1347
- firstItem: 0,
1348
- hasParallaxImages: false,
1349
- inactiveSlideOpacity: 0.7,
1350
- inactiveSlideScale: 0.9,
1351
- inactiveSlideShift: 0,
1352
- layout: 'default',
1353
- loop: false,
1354
- loopClonesPerSide: 3,
1355
- scrollEnabled: true,
1356
- slideStyle: {},
1357
- shouldOptimizeUpdates: true,
1358
- useScrollView: !Animated.FlatList,
1359
- vertical: false,
1360
- isCustomScrollWidth: false,
1361
- disableIntervalMomentum: IS_ANDROID,
1362
- useExperimentalSnap: IS_ANDROID,
1367
+ activeSlideAlignment: 'center',
1368
+ activeSlideOffset: 20,
1369
+ apparitionDelay: 0,
1370
+ autoplay: false,
1371
+ autoplayDelay: 1000,
1372
+ autoplayInterval: 3000,
1373
+ callbackOffsetMargin: 5,
1374
+ containerCustomStyle: {},
1375
+ contentContainerCustomStyle: {},
1376
+ enableSnap: true,
1377
+ firstItem: 0,
1378
+ hasParallaxImages: false,
1379
+ inactiveSlideOpacity: 0.7,
1380
+ inactiveSlideScale: 0.9,
1381
+ inactiveSlideShift: 0,
1382
+ layout: 'default',
1383
+ loop: false,
1384
+ loopClonesPerSide: 3,
1385
+ scrollEnabled: true,
1386
+ slideStyle: {},
1387
+ shouldOptimizeUpdates: true,
1388
+ useScrollView: !Animated.FlatList,
1389
+ vertical: false,
1390
+ isCustomScrollWidth: false,
1391
+ disableIntervalMomentum: IS_ANDROID,
1392
+ useExperimentalSnap: IS_ANDROID,
1363
1393
  };