Skip to content

Commit 16afa97

Browse files
authored
Support attaching animated value to Carousel scroll animated event (#1207)
1 parent 94e09bd commit 16afa97

File tree

5 files changed

+100
-56
lines changed

5 files changed

+100
-56
lines changed
Lines changed: 69 additions & 42 deletions
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,8 @@
11
import {Carousel, Constants, Text, View, Colors} from 'react-native-ui-lib';
22
import React, {Component} from 'react';
3+
import {StyleSheet, Animated, TextStyle} from 'react-native';
34
import _ from 'lodash';
4-
import {StyleSheet} from 'react-native';
5-
import {
6-
renderBooleanOption,
7-
renderSliderOption
8-
} from '../ExampleScreenPresenter';
9-
10-
interface Props {}
5+
import {renderBooleanOption, renderSliderOption} from '../ExampleScreenPresenter';
116

127
interface State {
138
numberOfPagesShown: number;
@@ -29,10 +24,11 @@ const BACKGROUND_COLORS = [
2924

3025
const pageHeight = Constants.windowHeight / 2;
3126

32-
class CarouselVerticalScreen extends Component<Props, State> {
27+
class CarouselVerticalScreen extends Component<{}, State> {
3328
carousel = React.createRef<typeof Carousel>();
29+
animatedScrollOffset = new Animated.ValueXY();
3430

35-
constructor(props: Props) {
31+
constructor(props: {}) {
3632
super(props);
3733

3834
this.state = {
@@ -41,44 +37,70 @@ class CarouselVerticalScreen extends Component<Props, State> {
4137
};
4238
}
4339

40+
renderAnimatedCounter = () => {
41+
const {numberOfPagesShown} = this.state;
42+
const animatedStyles = _.times(numberOfPagesShown, page => {
43+
return {
44+
opacity: this.animatedScrollOffset.y.interpolate({
45+
inputRange: [pageHeight * page - 50, pageHeight * page, pageHeight * page + 50],
46+
outputRange: [0, 1, 0]
47+
}),
48+
transform: [
49+
{
50+
translateX: this.animatedScrollOffset.y.interpolate({
51+
inputRange: [pageHeight * page - 50, pageHeight * page, pageHeight * page + 50],
52+
outputRange: [-50, 0, 50]
53+
})
54+
}
55+
]
56+
};
57+
});
58+
return (
59+
<View absT>
60+
{_.times(numberOfPagesShown, page => (
61+
<Text key={page} h1 animated style={[styles.animatedPageCounter, animatedStyles[page]] as TextStyle}>
62+
{page}
63+
</Text>
64+
))}
65+
</View>
66+
);
67+
};
68+
4469
render() {
45-
const {numberOfPagesShown, autoplay} = this.state
70+
const {numberOfPagesShown, autoplay} = this.state;
4671
return (
4772
<View flex paddingT-20>
4873
<View marginH-20 marginB-20>
4974
{renderBooleanOption.call(this, 'autoplay', 'autoplay')}
50-
{renderSliderOption.call(
51-
this,
52-
'Number of pages shown',
53-
'numberOfPagesShown',
54-
{
55-
min: 3,
56-
max: 10,
57-
step: 1,
58-
initial: 5
59-
}
60-
)}
75+
{renderSliderOption.call(this, 'Number of pages shown', 'numberOfPagesShown', {
76+
min: 3,
77+
max: 10,
78+
step: 1,
79+
initial: 5
80+
})}
81+
</View>
82+
<View>
83+
<Carousel
84+
key={'carousel'}
85+
ref={this.carousel}
86+
animatedScrollOffset={this.animatedScrollOffset}
87+
scrollEventThrottle={16}
88+
autoplay={autoplay}
89+
pageWidth={Constants.windowWidth}
90+
pageHeight={pageHeight}
91+
initialPage={0}
92+
containerStyle={{height: pageHeight}}
93+
allowAccessibleLayout
94+
horizontal={false}
95+
>
96+
{_.map([...Array(numberOfPagesShown)], (_, index) => (
97+
<Page style={{backgroundColor: BACKGROUND_COLORS[index]}} key={index}>
98+
<Text style={styles.pageText}>{index}</Text>
99+
</Page>
100+
))}
101+
</Carousel>
102+
{this.renderAnimatedCounter()}
61103
</View>
62-
<Carousel
63-
key={'carousel'}
64-
ref={this.carousel}
65-
autoplay={autoplay}
66-
pageWidth={Constants.windowWidth}
67-
pageHeight={pageHeight}
68-
initialPage={0}
69-
containerStyle={{height: pageHeight}}
70-
allowAccessibleLayout
71-
horizontal={false}
72-
>
73-
{_.map([...Array(numberOfPagesShown)], (_, index) => (
74-
<Page
75-
style={{backgroundColor: BACKGROUND_COLORS[index]}}
76-
key={index}
77-
>
78-
<Text style={styles.pageText}>{index}</Text>
79-
</Page>
80-
))}
81-
</Carousel>
82104
</View>
83105
);
84106
}
@@ -103,7 +125,12 @@ const styles = StyleSheet.create({
103125
pageText: {
104126
fontSize: 40,
105127
color: 'white'
128+
},
129+
animatedPageCounter: {
130+
position: 'absolute',
131+
top: 20,
132+
left: 20
106133
}
107134
});
108135

109-
export default CarouselVerticalScreen
136+
export default CarouselVerticalScreen;

generatedTypes/components/carousel/index.d.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -54,6 +54,7 @@ declare class Carousel extends Component<CarouselProps, CarouselState> {
5454
onMomentumScrollEnd: () => void;
5555
goToNextPage(): void;
5656
onScroll: (event: NativeSyntheticEvent<NativeScrollEvent>) => void;
57+
onScrollEvent: (...args: any[]) => void;
5758
renderChild: (child: ReactNode, key: Key) => JSX.Element | undefined;
5859
renderChildren(): JSX.Element[] | null | undefined;
5960
renderPageControl(): JSX.Element | undefined;

generatedTypes/components/carousel/types.d.ts

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,10 @@
1-
import { StyleProp, ViewStyle, NativeSyntheticEvent, NativeScrollEvent, PointPropType } from 'react-native';
1+
import { ScrollViewProps, StyleProp, ViewStyle, NativeSyntheticEvent, NativeScrollEvent, PointPropType, Animated } from 'react-native';
22
import { PageControlProps } from '../pageControl';
33
export declare enum PageControlPosition {
44
OVER = "over",
55
UNDER = "under"
66
}
7-
export interface CarouselProps {
7+
export interface CarouselProps extends ScrollViewProps {
88
/**
99
* the first page to start with
1010
*/
@@ -87,6 +87,11 @@ export interface CarouselProps {
8787
* instead of vertically in a column. The default value is true.
8888
*/
8989
horizontal?: boolean | null;
90+
/**
91+
* Pass to attach to ScrollView's Animated.event in order to animated elements base on
92+
* Carousel scroll offset (pass new Animated.ValueXY())
93+
*/
94+
animatedScrollOffset?: Animated.ValueXY;
9095
}
9196
export interface CarouselState {
9297
containerWidth?: number;

src/components/carousel/index.tsx

Lines changed: 16 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
import _ from 'lodash';
22
import React, {Component, RefObject, ReactNode, Key} from 'react';
3-
import {ScrollView, StyleSheet, LayoutChangeEvent, NativeSyntheticEvent, NativeScrollEvent} from 'react-native';
3+
import {Animated, ScrollView, StyleSheet, LayoutChangeEvent, NativeSyntheticEvent, NativeScrollEvent} from 'react-native';
44
import {Constants} from '../../helpers';
55
import {Colors} from '../../style';
66
import {asBaseComponent} from '../../commons/new';
@@ -319,6 +319,12 @@ class Carousel extends Component<CarouselProps, CarouselState> {
319319
_.invoke(this.props, 'onScroll', event);
320320
};
321321

322+
// @ts-ignore
323+
onScrollEvent = Animated.event([{nativeEvent: {contentOffset: {y: this.props?.animatedScrollOffset?.y, x: this.props?.animatedScrollOffset?.x}}}], {
324+
useNativeDriver: true,
325+
listener: this.onScroll
326+
});
327+
322328
renderChild = (child: ReactNode, key: Key): JSX.Element | undefined => {
323329
if (child) {
324330
const paddingLeft = this.props.horizontal ? this.shouldUsePageWidth() ? this.getItemSpacings(this.props) : undefined : 0;
@@ -434,33 +440,34 @@ class Carousel extends Component<CarouselProps, CarouselState> {
434440
}
435441

436442
renderCarousel() {
437-
const {containerStyle, animated, horizontal, ...others} = this.props;
443+
const {containerStyle, animated, horizontal, animatedScrollOffset, ...others} = this.props;
438444
const {initialOffset} = this.state;
439445
const scrollContainerStyle = this.shouldUsePageWidth()
440446
? {paddingRight: this.getItemSpacings(this.props)}
441447
: undefined;
442448
const snapToOffsets = this.getSnapToOffsets();
443449
const marginBottom = Math.max(0, this.getContainerPaddingVertical() - 16);
450+
const ScrollContainer = animatedScrollOffset ? Animated.ScrollView : ScrollView;
444451
return (
445452
<View animated={animated} style={[{marginBottom}, containerStyle]} onLayout={this.onContainerLayout}>
446-
<ScrollView
453+
<ScrollContainer
454+
showsHorizontalScrollIndicator={false}
455+
showsVerticalScrollIndicator={false}
456+
decelerationRate="fast"
457+
scrollEventThrottle={200}
447458
{...others}
448459
ref={this.carousel}
460+
onScroll={animatedScrollOffset ? this.onScrollEvent : this.onScroll}
449461
contentContainerStyle={scrollContainerStyle}
450462
horizontal={horizontal}
451-
showsHorizontalScrollIndicator={false}
452-
showsVerticalScrollIndicator={false}
453463
pagingEnabled={this.shouldEnablePagination()}
454464
snapToOffsets={snapToOffsets}
455-
decelerationRate="fast"
456465
contentOffset={initialOffset} // iOS only
457-
scrollEventThrottle={200}
458466
onContentSizeChange={this.onContentSizeChange}
459-
onScroll={this.onScroll}
460467
onMomentumScrollEnd={this.onMomentumScrollEnd}
461468
>
462469
{this.renderChildren()}
463-
</ScrollView>
470+
</ScrollContainer>
464471
{this.renderPageControl()}
465472
{this.renderCounter()}
466473
</View>

src/components/carousel/types.tsx

Lines changed: 7 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,13 @@
11
import React from 'react';
2-
import {StyleProp, ViewStyle, NativeSyntheticEvent, NativeScrollEvent, PointPropType} from 'react-native';
2+
import {ScrollViewProps, StyleProp, ViewStyle, NativeSyntheticEvent, NativeScrollEvent, PointPropType, Animated} from 'react-native';
33
import {PageControlProps} from '../pageControl';
44

55
export enum PageControlPosition {
66
OVER = 'over',
77
UNDER = 'under'
88
}
99

10-
export interface CarouselProps {
10+
export interface CarouselProps extends ScrollViewProps {
1111
/**
1212
* the first page to start with
1313
*/
@@ -85,12 +85,16 @@ export interface CarouselProps {
8585
* the amount of ms to wait before switching to the next page, in case autoplay is on
8686
*/
8787
autoplayInterval?: number;
88-
8988
/**
9089
* When true the scroll view's children are arranged horizontally in a row
9190
* instead of vertically in a column. The default value is true.
9291
*/
9392
horizontal?: boolean | null;
93+
/**
94+
* Pass to attach to ScrollView's Animated.event in order to animated elements base on
95+
* Carousel scroll offset (pass new Animated.ValueXY())
96+
*/
97+
animatedScrollOffset?: Animated.ValueXY;
9498
}
9599

96100
export interface CarouselState {

0 commit comments

Comments
 (0)