react-native-reanimated#onChange TypeScript Examples
The following examples show how to use
react-native-reanimated#onChange.
You can vote up the ones you like or vote down the ones you don't like,
and go to the original project or source file by following the links above each example. You may check out the related API usage on the sidebar.
Example #1
Source File: PaperOnboarding.tsx From react-native-paper-onboarding with MIT License | 4 votes |
PaperOnboardingComponent = forwardRef<
PaperOnboarding,
PaperOnboardingProps
>(
(
{
data,
safeInsets: _safeInsets,
direction = DEFAULT_DIRECTION,
// indicator config
indicatorSize = DEFAULT_INDICATOR_SIZE,
indicatorBackgroundColor = DEFAULT_INDICATOR_BACKGROUND_COLOR,
indicatorBorderColor = DEFAULT_INDICATOR_BORDER_COLOR,
// override styles
titleStyle,
descriptionStyle,
// close button config
closeButton,
closeButtonTextStyle,
closeButtonText = DEFAULT_CLOSE_BUTTON_TEXT,
onCloseButtonPress = DEFAULT_CLOSE_BUTTON_CALLBACK,
onIndexChange,
},
ref
) => {
// state
const [dimensions, setDimensions] =
useState<PaperOnboardingScreenDimensions>({
width: Dimensions.get('window').width,
height: Dimensions.get('window').height,
});
// refs
const indexRef = useRef<number>(0);
const pagesRef = useRef<Array<Animated.View | null>>(data.map(() => null));
//#region variables
const safeInsets = useMemo<Required<Insets>>(() => {
return {
top: _safeInsets?.top ?? DEFAULT_SAFE_INSET,
bottom: _safeInsets?.bottom ?? DEFAULT_SAFE_INSET,
left: _safeInsets?.left ?? DEFAULT_SAFE_INSET,
right: _safeInsets?.right ?? DEFAULT_SAFE_INSET,
};
}, [_safeInsets]);
const indicatorsContainerLeftPadding = useMemo(() => {
const containerLeftPadding = dimensions.width / 2 - indicatorSize / 2;
return I18nManager.isRTL
? -containerLeftPadding + indicatorSize * (data.length - 1)
: containerLeftPadding;
}, [dimensions.width, indicatorSize, data.length]);
//#endregion
//#region animated variables
const { gestureHandler, state, translation, velocity } =
usePanGestureHandler();
const animatedStaticIndex = useValue<number>(0);
const animatedOverrideIndex = useValue<number>(0);
const animatedIndex = useTiming({
animatedStaticIndex,
animatedOverrideIndex,
value: direction === 'horizontal' ? translation.x : translation.y,
velocity: direction === 'horizontal' ? velocity.x : velocity.y,
state: state,
size: data.length,
screenWidth: dimensions.width,
});
const indicatorsContainerPosition = useMemo(
() => data.map((_, index) => index * indicatorSize * -1),
[data, indicatorSize]
);
const animatedIndicatorsContainerPosition = useMemo(
() =>
add(
interpolate(animatedIndex, {
inputRange: data.map((_, index) => index),
outputRange: I18nManager.isRTL
? indicatorsContainerPosition.reverse()
: indicatorsContainerPosition,
extrapolate: Animated.Extrapolate.CLAMP,
}),
indicatorsContainerLeftPadding
),
[
data,
animatedIndex,
indicatorsContainerLeftPadding,
indicatorsContainerPosition,
]
);
//#endregion
//#region callbacks
const handlePageRef = useCallback((pageRef, index) => {
pagesRef.current[index] = pageRef;
}, []);
const handleOnLayout = useCallback(
({
nativeEvent: {
layout: { width, height },
},
}: LayoutChangeEvent) => {
setDimensions({
width,
height,
});
},
[]
);
//#endregion
//#region public methods
const handleNavigateToNextPage = useCallback(() => {
const currentIndex = indexRef.current;
if (currentIndex === data.length - 1) {
return;
}
animatedOverrideIndex.setValue(currentIndex + 1);
}, [data, animatedOverrideIndex]);
const handleNavigateToPreviousPage = useCallback(() => {
const currentIndex = indexRef.current;
if (currentIndex === 0) {
return;
}
animatedOverrideIndex.setValue(currentIndex - 1);
}, [animatedOverrideIndex]);
useImperativeHandle(
ref,
() => ({
next: handleNavigateToNextPage,
previous: handleNavigateToPreviousPage,
}),
[handleNavigateToNextPage, handleNavigateToPreviousPage]
);
//#endregion
//#region effects
useCode(
() =>
onChange(
animatedStaticIndex,
call([animatedStaticIndex], args => {
indexRef.current = args[0];
/**
* @DEV
* here we directly manipulate pages native props by setting `pointerEvents`
* to `auto` for current page and `none` for others.
*/
pagesRef.current.map((pageRef, _index) => {
// @ts-ignore
pageRef.setNativeProps({
pointerEvents: _index === args[0] ? 'auto' : 'none',
});
});
if (onIndexChange) {
onIndexChange(args[0]);
}
})
),
[]
);
//#endregion
// renders
return (
<PanGestureHandler {...gestureHandler}>
<Animated.View onLayout={handleOnLayout} style={styles.container}>
<Background
animatedIndex={animatedIndex}
data={data}
safeInsets={safeInsets}
screenDimensions={dimensions}
indicatorSize={indicatorSize}
animatedIndicatorsContainerPosition={
animatedIndicatorsContainerPosition
}
/>
{data.map((item, index) => (
<Page
key={`page-${index}`}
index={index}
item={item}
animatedIndex={animatedIndex}
indicatorSize={indicatorSize}
titleStyle={titleStyle}
descriptionStyle={descriptionStyle}
safeInsets={safeInsets}
screenDimensions={dimensions}
handleRef={handlePageRef}
/>
))}
<IndicatorsContainer
data={data}
animatedIndex={animatedIndex}
animatedIndicatorsContainerPosition={
animatedIndicatorsContainerPosition
}
indicatorSize={indicatorSize}
indicatorBackgroundColor={indicatorBackgroundColor}
indicatorBorderColor={indicatorBorderColor}
safeInsets={safeInsets}
/>
<CloseButton
data={data}
animatedIndex={animatedIndex}
safeInsets={safeInsets}
closeButtonText={closeButtonText}
closeButtonTextStyle={closeButtonTextStyle}
closeButton={closeButton}
onCloseButtonPress={onCloseButtonPress}
/>
</Animated.View>
</PanGestureHandler>
);
}
)
Example #2
Source File: useTiming.ts From react-native-paper-onboarding with MIT License | 4 votes |
useTiming = ({
animatedStaticIndex,
animatedOverrideIndex,
value,
velocity,
state,
size,
screenWidth,
}: useTimingProps) => {
const clock = useClock();
const isManuallyAnimated = useValue(0);
const config = useMemo(
() => ({
toValue: new Animated.Value(0),
duration: 500,
easing: Easing.out(Easing.exp),
}),
[]
);
const animationState = useMemo(
() => ({
finished: new Animated.Value(0),
position: new Animated.Value(0),
frameTime: new Animated.Value(0),
time: new Animated.Value(0),
}),
[]
);
const valueClamp = useMemo(
() =>
interpolate(value, {
inputRange: [screenWidth * -1, 0, screenWidth],
outputRange: I18nManager.isRTL ? [-1, 0, 1] : [1, 0, -1],
extrapolate: Animated.Extrapolate.CLAMP,
}),
[value, screenWidth]
);
const velocityClamp = useMemo(
() =>
interpolate(velocity, {
inputRange: [screenWidth * -2, 0, screenWidth * 2],
outputRange: I18nManager.isRTL ? [-0.5, 0, 0.5] : [0.5, 0, -0.5],
extrapolate: Animated.Extrapolate.CLAMP,
}),
[screenWidth, velocity]
);
const isTimingInterrupted = and(eq(state, State.BEGAN), clockRunning(clock));
const finishTiming = useMemo(
() => [
set(animatedStaticIndex, config.toValue),
set(animatedOverrideIndex, config.toValue),
set(animationState.frameTime, 0),
set(animationState.time, 0),
set(state, State.UNDETERMINED),
set(isManuallyAnimated, 0),
stopClock(clock),
],
[
state,
animatedOverrideIndex,
animatedStaticIndex,
animationState.frameTime,
animationState.time,
clock,
config.toValue,
isManuallyAnimated,
]
);
const shouldAnimate = useMemo(
() =>
and(
not(and(eq(animatedStaticIndex, 0), lessThan(valueClamp, 0))),
not(and(eq(animatedStaticIndex, size - 1), greaterThan(valueClamp, 0)))
),
[animatedStaticIndex, size, valueClamp]
);
const shouldReset = useMemo(
() => not(greaterThan(add(abs(valueClamp), abs(velocityClamp)), 0.5)),
[valueClamp, velocityClamp]
);
const shouldAnimateNext = useMemo(
() =>
greaterThan(
add(animationState.position, velocityClamp),
animatedStaticIndex
),
[animatedStaticIndex, animationState.position, velocityClamp]
);
const animatedPosition = useMemo(
() =>
block([
cond(isTimingInterrupted, finishTiming),
cond(
eq(state, State.ACTIVE),
cond(
and(
not(and(eq(animatedStaticIndex, 0), lessThan(valueClamp, 0))),
not(
and(
eq(animatedStaticIndex, size - 1),
greaterThan(valueClamp, 0)
)
)
),
[
set(animationState.finished, 0),
set(
animationState.position,
add(animatedStaticIndex, valueClamp)
),
]
)
),
onChange(animatedOverrideIndex, [
set(isManuallyAnimated, 1),
set(animationState.finished, 0),
]),
cond(or(eq(state, State.END), isManuallyAnimated), [
cond(and(not(clockRunning(clock)), not(animationState.finished)), [
cond(
isManuallyAnimated,
set(config.toValue, animatedOverrideIndex),
cond(
or(shouldReset, not(shouldAnimate)),
set(config.toValue, animatedStaticIndex),
cond(
shouldAnimateNext,
set(config.toValue, add(animatedStaticIndex, 1)),
set(config.toValue, sub(animatedStaticIndex, 1))
)
)
),
set(animationState.finished, 0),
set(animationState.frameTime, 0),
set(animationState.time, 0),
startClock(clock),
]),
timing(clock, animationState, config),
cond(animationState.finished, finishTiming),
]),
animationState.position,
]),
[
size,
state,
animatedOverrideIndex,
animatedStaticIndex,
animationState,
clock,
config,
finishTiming,
isManuallyAnimated,
isTimingInterrupted,
shouldAnimate,
shouldAnimateNext,
shouldReset,
valueClamp,
]
);
return animatedPosition;
}
Example #3
Source File: StickyItemFlatList.tsx From react-native-sticky-item with MIT License | 4 votes |
StickyItemFlatList = forwardRef( <T extends {}>(props: StickyItemFlatListProps<T>, ref: Ref<FlatList<T>>) => { const { initialScrollIndex = 0, decelerationRate = DEFAULT_DECELERATION_RATE, itemWidth, itemHeight, separatorSize = DEFAULT_SEPARATOR_SIZE, borderRadius = DEFAULT_BORDER_RADIUS, stickyItemActiveOpacity = DEFAULT_STICKY_ITEM_ACTIVE_OPACITY, stickyItemWidth, stickyItemHeight, stickyItemBackgroundColors, stickyItemContent, onStickyItemPress, isRTL = DEFAULT_IS_RTL, ItemSeparatorComponent = Separator, ...rest } = props; // refs const flatListRef = useRef<FlatList<T>>(null); const tapRef = useRef<TapGestureHandler>(null); //#region variables const itemWidthWithSeparator = useMemo( () => itemWidth + separatorSize, [itemWidth, separatorSize] ); const separatorProps = useMemo( () => ({ size: separatorSize, }), [separatorSize] ); //#endregion //#region styles const contentContainerStyle = useMemo( () => [ rest.contentContainerStyle, { paddingLeft: itemWidth + separatorSize * 2, paddingRight: separatorSize, }, ], [rest.contentContainerStyle, itemWidth, separatorSize] ); //#endregion //#region methods const getHitSlop = useCallback( isMinimized => { const verticalPosition = isMinimized ? -((itemHeight - stickyItemHeight) / 2) : 0; const startPosition = isMinimized ? 0 : -separatorSize; const endPosition = isMinimized ? -(SCREEN_WIDTH - stickyItemWidth) : -(SCREEN_WIDTH - separatorSize - itemWidth); return { top: verticalPosition, right: isRTL ? startPosition : endPosition, left: isRTL ? endPosition : startPosition, bottom: verticalPosition, }; }, [ itemWidth, itemHeight, stickyItemWidth, stickyItemHeight, separatorSize, isRTL, ] ); const getItemLayout = useCallback( (_, index) => { return { length: itemWidthWithSeparator, // sticky item + previous items width offset: itemWidthWithSeparator + itemWidthWithSeparator * index, index, }; }, [itemWidthWithSeparator] ); //#endregion //#region gesture const x = useValue(0); const tapState = useValue(State.UNDETERMINED); const tapGestures = useGestureHandler({ state: tapState }); const onScroll = event([ { nativeEvent: { contentOffset: { x, }, }, }, ]); const onScrollEnd = event([ { nativeEvent: { contentOffset: { x, }, }, }, ]); //#endregion //#region effects //@ts-ignore useImperativeHandle(ref, () => flatListRef.current!.getNode()); useCode( () => cond(eq(tapState, State.END), [ call([tapState], () => { if (onStickyItemPress) { onStickyItemPress(); } }), set(tapState, State.UNDETERMINED), ]), [tapState] ); useCode( () => onChange( x, call([x], args => { if (tapRef.current) { const isMinimized = args[0] > 0; // @ts-ignore tapRef.current.setNativeProps({ hitSlop: getHitSlop(isMinimized), }); } }) ), [ x, itemWidth, itemHeight, stickyItemWidth, stickyItemWidth, separatorSize, ] ); useEffect(() => { /** * @DEV * to fix stick item position with fast refresh */ x.setValue(0); if (tapRef.current) { // @ts-ignore tapRef.current.setNativeProps({ hitSlop: getHitSlop(initialScrollIndex !== 0), }); } // eslint-disable-next-line react-hooks/exhaustive-deps }, [getHitSlop]); //#endregion // render const renderSeparator = useCallback(() => { if (typeof ItemSeparatorComponent === 'function') { // @ts-ignore return ItemSeparatorComponent(separatorProps); } else { // @ts-ignore return <ItemSeparatorComponent size={separatorProps.size} />; } }, [ItemSeparatorComponent, separatorProps]); return ( <TapGestureHandler ref={tapRef} waitFor={flatListRef} shouldCancelWhenOutside={true} {...tapGestures} > <Animated.View> <AnimatedFlatList {...rest} ref={flatListRef} initialScrollIndex={initialScrollIndex} inverted={isRTL} ItemSeparatorComponent={renderSeparator} contentContainerStyle={contentContainerStyle} horizontal={true} showsHorizontalScrollIndicator={false} scrollEventThrottle={1} pagingEnabled={true} decelerationRate={decelerationRate} snapToAlignment={'start'} snapToInterval={itemWidth + separatorSize} onScroll={onScroll} onScrollAnimationEnd={onScrollEnd} getItemLayout={getItemLayout} /> <StickyItem x={x} tapState={tapState} itemWidth={itemWidth} itemHeight={itemHeight} separatorSize={separatorSize} borderRadius={borderRadius} stickyItemActiveOpacity={stickyItemActiveOpacity} stickyItemWidth={stickyItemWidth} stickyItemHeight={stickyItemHeight} stickyItemBackgroundColors={stickyItemBackgroundColors} stickyItemContent={stickyItemContent} isRTL={isRTL} /> </Animated.View> </TapGestureHandler> ); } )
Example #4
Source File: index.tsx From react-native-scroll-bottom-sheet with MIT License | 4 votes |
render() {
const {
renderHandle,
snapPoints,
initialSnapIndex,
componentType,
onSettle,
animatedPosition,
containerStyle,
...rest
} = this.props;
const AnimatedScrollableComponent = this.scrollComponent;
const normalisedSnapPoints = this.getNormalisedSnapPoints();
const initialSnap = normalisedSnapPoints[initialSnapIndex];
const Content = (
<Animated.View
style={[
StyleSheet.absoluteFillObject,
containerStyle,
// @ts-ignore
{
transform: [{ translateY: this.translateY }],
},
]}
>
<PanGestureHandler
ref={this.drawerHandleRef}
shouldCancelWhenOutside={false}
simultaneousHandlers={this.masterDrawer}
onGestureEvent={this.onHandleGestureEvent}
onHandlerStateChange={this.onHandleGestureEvent}
>
<Animated.View>{renderHandle()}</Animated.View>
</PanGestureHandler>
<PanGestureHandler
ref={this.drawerContentRef}
simultaneousHandlers={[this.scrollComponentRef, this.masterDrawer]}
shouldCancelWhenOutside={false}
onGestureEvent={this.onDrawerGestureEvent}
onHandlerStateChange={this.onDrawerGestureEvent}
>
<Animated.View style={styles.container}>
<NativeViewGestureHandler
ref={this.scrollComponentRef}
waitFor={this.masterDrawer}
simultaneousHandlers={this.drawerContentRef}
>
<AnimatedScrollableComponent
overScrollMode="never"
bounces={false}
{...rest}
ref={this.props.innerRef}
// @ts-ignore
decelerationRate={this.decelerationRate}
onScrollBeginDrag={this.onScrollBeginDrag}
scrollEventThrottle={1}
contentContainerStyle={[
rest.contentContainerStyle,
{ paddingBottom: this.getNormalisedSnapPoints()[0] },
]}
/>
</NativeViewGestureHandler>
</Animated.View>
</PanGestureHandler>
{this.props.animatedPosition && (
<Animated.Code
exec={onChange(
this.position,
set(this.props.animatedPosition, this.position)
)}
/>
)}
<Animated.Code
exec={onChange(
this.dragY,
cond(not(eq(this.dragY, 0)), set(this.prevDragY, this.dragY))
)}
/>
<Animated.Code
exec={onChange(
this.didGestureFinish,
cond(this.didGestureFinish, [
this.didScrollUpAndPullDown,
this.setTranslationY,
set(
this.tempDestSnapPoint,
add(normalisedSnapPoints[0], this.extraOffset)
),
set(this.nextSnapIndex, 0),
set(this.destSnapPoint, this.calculateNextSnapPoint()),
cond(
and(
greaterThan(this.dragY, this.lastStartScrollY),
this.isAndroid,
not(this.dragWithHandle)
),
call([], () => {
// This prevents the scroll glide from happening on Android when pulling down with inertia.
// It's not perfect, but does the job for now
const { method, args } = imperativeScrollOptions[
this.props.componentType
];
// @ts-ignore
const node = this.props.innerRef.current?.getNode();
if (
node &&
node[method] &&
((this.props.componentType === 'FlatList' &&
(this.props?.data?.length || 0) > 0) ||
(this.props.componentType === 'SectionList' &&
this.props.sections.length > 0) ||
this.props.componentType === 'ScrollView')
) {
node[method](args);
}
})
),
set(this.dragY, 0),
set(this.velocityY, 0),
set(
this.lastSnap,
sub(
this.destSnapPoint,
cond(
eq(this.scrollUpAndPullDown, 1),
this.lastStartScrollY,
0
)
)
),
call([this.lastSnap], ([value]) => {
// This is the TapGHandler trick
// @ts-ignore
this.masterDrawer?.current?.setNativeProps({
maxDeltaY: value - this.getNormalisedSnapPoints()[0],
});
}),
set(
this.decelerationRate,
cond(
eq(this.isAndroid, 1),
cond(
eq(this.lastSnap, normalisedSnapPoints[0]),
ANDROID_NORMAL_DECELERATION_RATE,
0
),
IOS_NORMAL_DECELERATION_RATE
)
),
])
)}
/>
<Animated.Code
exec={onChange(this.isManuallySetValue, [
cond(
this.isManuallySetValue,
[
set(this.destSnapPoint, this.manualYOffset),
set(this.animationFinished, 0),
set(this.lastSnap, this.manualYOffset),
call([this.lastSnap], ([value]) => {
// This is the TapGHandler trick
// @ts-ignore
this.masterDrawer?.current?.setNativeProps({
maxDeltaY: value - this.getNormalisedSnapPoints()[0],
});
}),
],
[set(this.nextSnapIndex, 0)]
),
])}
/>
</Animated.View>
);
// On Android, having an intermediary view with pointerEvents="box-none", breaks the
// waitFor logic
if (Platform.OS === 'android') {
return (
<TapGestureHandler
maxDurationMs={100000}
ref={this.masterDrawer}
maxDeltaY={initialSnap - this.getNormalisedSnapPoints()[0]}
shouldCancelWhenOutside={false}
>
{Content}
</TapGestureHandler>
);
}
// On iOS, We need to wrap the content on a view with PointerEvents box-none
// So that we can start scrolling automatically when reaching the top without
// Stopping the gesture
return (
<TapGestureHandler
maxDurationMs={100000}
ref={this.masterDrawer}
maxDeltaY={initialSnap - this.getNormalisedSnapPoints()[0]}
>
<View style={StyleSheet.absoluteFillObject} pointerEvents="box-none">
{Content}
</View>
</TapGestureHandler>
);
}