@momo-kits/carousel 0.0.65-alpha.11 → 0.0.65-alpha.13

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