react-native-reanimated#add TypeScript Examples
The following examples show how to use
react-native-reanimated#add.
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: BackgroundCircle.tsx From react-native-paper-onboarding with MIT License | 5 votes |
BackgroundCircleComponent = ({
index,
animatedIndex,
color,
extendedSize,
bottomPosition,
screenDimensions,
indicatorSize,
animatedIndicatorsContainerPosition,
}: BackgroundCircleProps) => {
//#region variables
//#endregion
//#region animations
const animatedFocus = useMemo(
() =>
interpolate(animatedIndex, {
inputRange: [index - 1, index, index + 1],
outputRange: [0, 1, 2],
extrapolate: Extrapolate.CLAMP,
}),
[animatedIndex, index]
);
const animatedRadius = useMemo(
() =>
interpolate(animatedFocus, {
inputRange: [0, 1],
outputRange: [0, extendedSize],
extrapolate: Extrapolate.CLAMP,
}),
[animatedFocus, extendedSize]
);
const animatedLeftPosition = useMemo(
() =>
add(
animatedIndicatorsContainerPosition,
indicatorSize / 2,
I18nManager.isRTL
? -((index + 1) * indicatorSize)
: index * indicatorSize,
I18nManager.isRTL ? screenDimensions.width : 0
),
[
animatedIndicatorsContainerPosition,
index,
indicatorSize,
screenDimensions.width,
]
);
//#endregion
// render
return (
<AnimatedCircle
r={animatedRadius}
cy={bottomPosition}
cx={animatedLeftPosition}
fill={color}
/>
);
}
Example #2
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 #3
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 #4
Source File: StickyItem.tsx From react-native-sticky-item with MIT License | 4 votes |
StickyItem = ({
x,
tapState,
itemWidth,
itemHeight,
separatorSize,
borderRadius,
stickyItemActiveOpacity,
stickyItemWidth,
stickyItemHeight,
stickyItemContent: StickyItemContent,
stickyItemBackgroundColors,
isRTL,
}: StickyItemProps) => {
const threshold = itemWidth - stickyItemWidth + separatorSize;
//#region animations
const animatedTranslateX = multiply(
cond(
greaterThan(x, threshold),
isRTL ? SCREEN_WIDTH - stickyItemWidth : itemWidth - stickyItemWidth,
isRTL
? add(x, SCREEN_WIDTH - itemWidth - separatorSize)
: sub(x, separatorSize)
),
isRTL ? 1 : -1
);
const animatedOpacity = withTimingTransition(eq(tapState, 2), {
duration: 125,
easing: Easing.inOut(Easing.quad),
});
//#endregion
//#region styles
const containerStyle = useMemo(
() => [
styles.container,
{
width: itemWidth,
height: itemHeight,
opacity: interpolate(animatedOpacity, {
inputRange: [0, 1],
outputRange: [1, stickyItemActiveOpacity],
extrapolate: Extrapolate.CLAMP,
}),
transform: transformOrigin(
{ x: itemWidth / 2, y: 0 },
{
translateX: animatedTranslateX,
}
) as Animated.AnimatedTransform,
},
],
// eslint-disable-next-line react-hooks/exhaustive-deps
[itemWidth, itemHeight, stickyItemActiveOpacity]
);
const pressableViewStyle = useMemo<ViewStyle>(
() => ({
...styles.pressable,
width: stickyItemWidth,
height: stickyItemHeight,
borderRadius: stickyItemWidth,
[isRTL ? 'left' : 'right']: 0,
top: (itemHeight - stickyItemHeight) / 2,
}),
[isRTL, stickyItemWidth, stickyItemHeight, itemHeight]
);
//#endregion
// render
const renderContent = () => {
const props = {
x,
itemWidth,
itemHeight,
stickyItemWidth,
stickyItemHeight,
separatorSize,
borderRadius,
threshold,
isRTL,
};
return typeof StickyItemContent === 'function' ? (
// @ts-ignore
StickyItemContent(props)
) : (
// @ts-ignore
<StickyItemContent {...props} />
);
};
return (
<Animated.View pointerEvents="box-none" style={containerStyle}>
<View style={pressableViewStyle} />
<StickyItemBackground
threshold={threshold}
x={x}
itemWidth={itemWidth}
itemHeight={itemHeight}
separatorSize={separatorSize}
borderRadius={borderRadius}
stickyItemWidth={stickyItemWidth}
stickyItemHeight={stickyItemHeight}
stickyItemBackgroundColors={stickyItemBackgroundColors}
isRTL={isRTL}
/>
{renderContent()}
</Animated.View>
);
}
Example #5
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>
);
}
Example #6
Source File: index.tsx From react-native-scroll-bottom-sheet with MIT License | 4 votes |
constructor(props: Props<T>) {
super(props);
const { initialSnapIndex, animationType } = props;
const animationDriver = animationType === 'timing' ? 0 : 1;
const animationDuration =
(props.animationType === 'timing' && props.animationConfig?.duration) ||
DEFAULT_ANIMATION_DURATION;
const ScrollComponent = this.getScrollComponent();
// @ts-ignore
this.scrollComponent = Animated.createAnimatedComponent(ScrollComponent);
const snapPoints = this.getNormalisedSnapPoints();
const openPosition = snapPoints[0];
const closedPosition = this.props.enableOverScroll
? windowHeight
: snapPoints[snapPoints.length - 1];
const initialSnap = snapPoints[initialSnapIndex];
this.nextSnapIndex = new Value(initialSnapIndex);
const initialDecelerationRate = Platform.select({
android:
props.initialSnapIndex === 0 ? ANDROID_NORMAL_DECELERATION_RATE : 0,
ios: IOS_NORMAL_DECELERATION_RATE,
});
this.decelerationRate = new Value(initialDecelerationRate);
const handleGestureState = new Value<GestureState>(-1);
const handleOldGestureState = new Value<GestureState>(-1);
const drawerGestureState = new Value<GestureState>(-1);
const drawerOldGestureState = new Value<GestureState>(-1);
const lastSnapInRange = new Value(1);
this.prevTranslateYOffset = new Value(initialSnap);
this.translationY = new Value(initialSnap);
this.lastSnap = new Value(initialSnap);
this.onHandleGestureEvent = event([
{
nativeEvent: {
translationY: this.dragY,
oldState: handleOldGestureState,
state: handleGestureState,
velocityY: this.velocityY,
},
},
]);
this.onDrawerGestureEvent = event([
{
nativeEvent: {
translationY: this.dragY,
oldState: drawerOldGestureState,
state: drawerGestureState,
velocityY: this.velocityY,
},
},
]);
this.onScrollBeginDrag = event([
{
nativeEvent: {
contentOffset: { y: this.lastStartScrollY },
},
},
]);
const didHandleGestureBegin = eq(handleGestureState, GestureState.ACTIVE);
const isAnimationInterrupted = and(
or(
eq(handleGestureState, GestureState.BEGAN),
eq(drawerGestureState, GestureState.BEGAN),
and(
eq(this.isAndroid, 0),
eq(animationDriver, 1),
or(
eq(drawerGestureState, GestureState.ACTIVE),
eq(handleGestureState, GestureState.ACTIVE)
)
)
),
clockRunning(this.animationClock)
);
this.didGestureFinish = or(
and(
eq(handleOldGestureState, GestureState.ACTIVE),
eq(handleGestureState, GestureState.END)
),
and(
eq(drawerOldGestureState, GestureState.ACTIVE),
eq(drawerGestureState, GestureState.END)
)
);
// Function that determines if the last snap point is in the range {snapPoints}
// In the case of interruptions in the middle of an animation, we'll get
// lastSnap values outside the range
const isLastSnapPointInRange = (i: number = 0): Animated.Node<number> =>
i === snapPoints.length
? lastSnapInRange
: cond(
eq(this.lastSnap, snapPoints[i]),
[set(lastSnapInRange, 1)],
isLastSnapPointInRange(i + 1)
);
const scrollY = [
set(lastSnapInRange, 0),
isLastSnapPointInRange(),
cond(
or(
didHandleGestureBegin,
and(
this.isManuallySetValue,
not(eq(this.manualYOffset, snapPoints[0]))
)
),
[set(this.dragWithHandle, 1), 0]
),
cond(
// This is to account for a continuous scroll on the drawer from a snap point
// Different than top, bringing the drawer to the top position, so that if we
// change scroll direction without releasing the gesture, it doesn't pull down the drawer again
and(
eq(this.dragWithHandle, 1),
greaterThan(snapPoints[0], add(this.lastSnap, this.dragY)),
and(not(eq(this.lastSnap, snapPoints[0])), lastSnapInRange)
),
[
set(this.lastSnap, snapPoints[0]),
set(this.dragWithHandle, 0),
this.lastStartScrollY,
],
cond(eq(this.dragWithHandle, 1), 0, this.lastStartScrollY)
),
];
this.didScrollUpAndPullDown = cond(
and(
greaterOrEq(this.dragY, this.lastStartScrollY),
greaterThan(this.lastStartScrollY, 0)
),
set(this.scrollUpAndPullDown, 1)
);
this.setTranslationY = cond(
and(
not(this.dragWithHandle),
not(greaterOrEq(this.dragY, this.lastStartScrollY))
),
set(this.translationY, sub(this.dragY, this.lastStartScrollY)),
set(this.translationY, this.dragY)
);
this.extraOffset = cond(
eq(this.scrollUpAndPullDown, 1),
this.lastStartScrollY,
0
);
const endOffsetY = add(
this.lastSnap,
this.translationY,
multiply(1 - props.friction, this.velocityY)
);
this.calculateNextSnapPoint = (i = 0): Animated.Node<number> | number =>
i === snapPoints.length
? this.tempDestSnapPoint
: cond(
greaterThan(
abs(sub(this.tempDestSnapPoint, endOffsetY)),
abs(sub(add(snapPoints[i], this.extraOffset), endOffsetY))
),
[
set(this.tempDestSnapPoint, add(snapPoints[i], this.extraOffset)),
set(this.nextSnapIndex, i),
this.calculateNextSnapPoint(i + 1),
],
this.calculateNextSnapPoint(i + 1)
);
const runAnimation = ({
clock,
from,
to,
position,
finished,
velocity,
frameTime,
}: TimingParams) => {
const state = {
finished,
velocity: new Value(0),
position,
time: new Value(0),
frameTime,
};
const timingConfig = {
duration: animationDuration,
easing:
(props.animationType === 'timing' && props.animationConfig?.easing) ||
DEFAULT_EASING,
toValue: new Value(0),
};
const springConfig = {
...DEFAULT_SPRING_PARAMS,
...((props.animationType === 'spring' && props.animationConfig) || {}),
toValue: new Value(0),
};
return [
cond(and(not(clockRunning(clock)), not(eq(finished, 1))), [
// If the clock isn't running, we reset all the animation params and start the clock
set(state.finished, 0),
set(state.velocity, velocity),
set(state.time, 0),
set(state.position, from),
set(state.frameTime, 0),
set(timingConfig.toValue, to),
set(springConfig.toValue, to),
startClock(clock),
]),
// We run the step here that is going to update position
cond(
eq(animationDriver, 0),
timing(clock, state, timingConfig),
spring(clock, state, springConfig)
),
cond(
state.finished,
[
call([this.nextSnapIndex], ([value]) => {
if (value !== this.prevSnapIndex) {
this.props.onSettle?.(value);
}
this.prevSnapIndex = value;
}),
// Resetting appropriate values
set(drawerOldGestureState, GestureState.END),
set(handleOldGestureState, GestureState.END),
set(this.prevTranslateYOffset, state.position),
cond(eq(this.scrollUpAndPullDown, 1), [
set(
this.prevTranslateYOffset,
sub(this.prevTranslateYOffset, this.lastStartScrollY)
),
set(this.lastStartScrollY, 0),
set(this.scrollUpAndPullDown, 0),
]),
cond(eq(this.destSnapPoint, snapPoints[0]), [
set(this.dragWithHandle, 0),
]),
set(this.isManuallySetValue, 0),
set(this.manualYOffset, 0),
stopClock(clock),
this.prevTranslateYOffset,
],
// We made the block return the updated position,
state.position
),
];
};
const translateYOffset = cond(
isAnimationInterrupted,
[
// set(prevTranslateYOffset, animationPosition) should only run if we are
// interrupting an animation when the drawer is currently in a different
// position than the top
cond(
or(
this.dragWithHandle,
greaterOrEq(abs(this.prevDragY), this.lastStartScrollY)
),
set(this.prevTranslateYOffset, this.animationPosition)
),
set(this.animationFinished, 1),
set(this.translationY, 0),
// Resetting appropriate values
set(drawerOldGestureState, GestureState.END),
set(handleOldGestureState, GestureState.END),
// By forcing that frameTime exceeds duration, it has the effect of stopping the animation
set(this.animationFrameTime, add(animationDuration, 1000)),
set(this.velocityY, 0),
stopClock(this.animationClock),
this.prevTranslateYOffset,
],
cond(
or(
this.didGestureFinish,
this.isManuallySetValue,
clockRunning(this.animationClock)
),
[
runAnimation({
clock: this.animationClock,
from: cond(
this.isManuallySetValue,
this.prevTranslateYOffset,
add(this.prevTranslateYOffset, this.translationY)
),
to: this.destSnapPoint,
position: this.animationPosition,
finished: this.animationFinished,
frameTime: this.animationFrameTime,
velocity: this.velocityY,
}),
],
[
set(this.animationFrameTime, 0),
set(this.animationFinished, 0),
// @ts-ignore
this.prevTranslateYOffset,
]
)
);
this.translateY = interpolate(
add(translateYOffset, this.dragY, multiply(scrollY, -1)),
{
inputRange: [openPosition, closedPosition],
outputRange: [openPosition, closedPosition],
extrapolate: Extrapolate.CLAMP,
}
);
this.position = interpolate(this.translateY, {
inputRange: [openPosition, snapPoints[snapPoints.length - 1]],
outputRange: [1, 0],
extrapolate: Extrapolate.CLAMP,
});
}