Skip to content

Commit

Permalink
Finish migrating to new reanimated
Browse files Browse the repository at this point in the history
  • Loading branch information
blazejkustra committed Nov 5, 2024
1 parent 0667cf7 commit 73c358f
Show file tree
Hide file tree
Showing 18 changed files with 182 additions and 188 deletions.
88 changes: 46 additions & 42 deletions src/components/MultiGestureCanvas/usePinchGesture.ts
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
/* eslint-disable no-param-reassign */
import {useEffect, useState} from 'react';
import {useCallback, useEffect, useState} from 'react';
import type {PinchGesture} from 'react-native-gesture-handler';
import {Gesture} from 'react-native-gesture-handler';
import {runOnJS, useAnimatedReaction, useSharedValue, useWorkletCallback, withSpring} from 'react-native-reanimated';
import {runOnJS, useAnimatedReaction, useSharedValue, withSpring} from 'react-native-reanimated';
import {SPRING_CONFIG, ZOOM_RANGE_BOUNCE_FACTORS} from './constants';
import type {MultiGestureCanvasVariables} from './types';

Expand Down Expand Up @@ -61,29 +61,33 @@ const usePinchGesture = ({
return;
}

runOnJS(onScaleChanged)(zoomScale.value);
runOnJS(onScaleChanged)(zoomScale.get());
};

// Update the total (pinch) translation based on the regular pinch + bounce
useAnimatedReaction(
() => [pinchTranslateX.value, pinchTranslateY.value, pinchBounceTranslateX.value, pinchBounceTranslateY.value],
() => [pinchTranslateX.get(), pinchTranslateY.get(), pinchBounceTranslateX.get(), pinchBounceTranslateY.get()],
([translateX, translateY, bounceX, bounceY]) => {
// eslint-disable-next-line react-compiler/react-compiler
totalPinchTranslateX.value = translateX + bounceX;
totalPinchTranslateY.value = translateY + bounceY;
totalPinchTranslateX.set(translateX + bounceX);
totalPinchTranslateY.set(translateY + bounceY);
},
);

/**
* Calculates the adjusted focal point of the pinch gesture,
* based on the canvas size and the current offset
*/
const getAdjustedFocal = useWorkletCallback(
(focalX: number, focalY: number) => ({
x: focalX - (canvasSize.width / 2 + offsetX.value),
y: focalY - (canvasSize.height / 2 + offsetY.value),
}),
[canvasSize.width, canvasSize.height],
const getAdjustedFocal = useCallback(
(focalX: number, focalY: number) => {
'worklet';

return {
x: focalX - (canvasSize.width / 2 + offsetX.get()),
y: focalY - (canvasSize.height / 2 + offsetY.get()),
};
},
[canvasSize.width, canvasSize.height, offsetX, offsetY],
);

// The pinch gesture is disabled when we release one of the fingers
Expand All @@ -101,7 +105,7 @@ const usePinchGesture = ({
// The first argument is not used, but must be defined
.onTouchesDown((_evt, state) => {
// We don't want to activate pinch gesture when we are swiping in the pager
if (!shouldDisableTransformationGestures.value) {
if (!shouldDisableTransformationGestures.get()) {
return;
}

Expand All @@ -112,8 +116,8 @@ const usePinchGesture = ({

// Set the origin focal point of the pinch gesture at the start of the gesture
const adjustedFocal = getAdjustedFocal(evt.focalX, evt.focalY);
pinchOrigin.x.value = adjustedFocal.x;
pinchOrigin.y.value = adjustedFocal.y;
pinchOrigin.x.set(adjustedFocal.x);
pinchOrigin.y.set(adjustedFocal.y);
})
.onChange((evt) => {
// Disable the pinch gesture if one finger is released,
Expand All @@ -123,58 +127,58 @@ const usePinchGesture = ({
return;
}

const newZoomScale = pinchScale.value * evt.scale;

const newZoomScale = pinchScale.get() * evt.scale;
const zoomScaleValue = zoomScale.get();
// Limit the zoom scale to zoom range including bounce range
if (zoomScale.value >= zoomRange.min * ZOOM_RANGE_BOUNCE_FACTORS.min && zoomScale.value <= zoomRange.max * ZOOM_RANGE_BOUNCE_FACTORS.max) {
zoomScale.value = newZoomScale;
currentPinchScale.value = evt.scale;
if (zoomScaleValue >= zoomRange.min * ZOOM_RANGE_BOUNCE_FACTORS.min && zoomScaleValue <= zoomRange.max * ZOOM_RANGE_BOUNCE_FACTORS.max) {
zoomScale.set(newZoomScale);
currentPinchScale.set(evt.scale);

triggerScaleChangedEvent();
}

// Calculate new pinch translation
const adjustedFocal = getAdjustedFocal(evt.focalX, evt.focalY);
const newPinchTranslateX = adjustedFocal.x + currentPinchScale.value * pinchOrigin.x.value * -1;
const newPinchTranslateY = adjustedFocal.y + currentPinchScale.value * pinchOrigin.y.value * -1;
const newPinchTranslateX = adjustedFocal.x + currentPinchScale.get() * pinchOrigin.x.get() * -1;
const newPinchTranslateY = adjustedFocal.y + currentPinchScale.get() * pinchOrigin.y.get() * -1;

// If the zoom scale is within the zoom range, we perform the regular pinch translation
// Otherwise it means that we are "overzoomed" or "underzoomed", so we need to bounce back
if (zoomScale.value >= zoomRange.min && zoomScale.value <= zoomRange.max) {
pinchTranslateX.value = newPinchTranslateX;
pinchTranslateY.value = newPinchTranslateY;
if (zoomScaleValue >= zoomRange.min && zoomScaleValue <= zoomRange.max) {
pinchTranslateX.set(newPinchTranslateX);
pinchTranslateY.set(newPinchTranslateY);
} else {
// Store x and y translation that is produced while bouncing
// so we can revert the bounce once pinch gesture is released
pinchBounceTranslateX.value = newPinchTranslateX - pinchTranslateX.value;
pinchBounceTranslateY.value = newPinchTranslateY - pinchTranslateY.value;
pinchBounceTranslateX.set(newPinchTranslateX - pinchTranslateX.get());
pinchBounceTranslateY.set(newPinchTranslateY - pinchTranslateY.get());
}
})
.onEnd(() => {
// Add pinch translation to total offset and reset gesture variables
offsetX.value += pinchTranslateX.value;
offsetY.value += pinchTranslateY.value;
pinchTranslateX.value = 0;
pinchTranslateY.value = 0;
currentPinchScale.value = 1;
offsetX.set((value) => value + pinchTranslateX.get());
offsetY.set((value) => value + pinchTranslateY.get());
pinchTranslateX.set(0);
pinchTranslateY.set(0);
currentPinchScale.set(1);

// If the content was "overzoomed" or "underzoomed", we need to bounce back with an animation
if (pinchBounceTranslateX.value !== 0 || pinchBounceTranslateY.value !== 0) {
pinchBounceTranslateX.value = withSpring(0, SPRING_CONFIG);
pinchBounceTranslateY.value = withSpring(0, SPRING_CONFIG);
if (pinchBounceTranslateX.get() !== 0 || pinchBounceTranslateY.get() !== 0) {
pinchBounceTranslateX.set(withSpring(0, SPRING_CONFIG));
pinchBounceTranslateY.set(withSpring(0, SPRING_CONFIG));
}

if (zoomScale.value < zoomRange.min) {
if (zoomScale.get() < zoomRange.min) {
// If the zoom scale is less than the minimum zoom scale, we need to set the zoom scale to the minimum
pinchScale.value = zoomRange.min;
zoomScale.value = withSpring(zoomRange.min, SPRING_CONFIG, triggerScaleChangedEvent);
} else if (zoomScale.value > zoomRange.max) {
pinchScale.set(zoomRange.min);
zoomScale.set(withSpring(zoomRange.min, SPRING_CONFIG, triggerScaleChangedEvent));
} else if (zoomScale.get() > zoomRange.max) {
// If the zoom scale is higher than the maximum zoom scale, we need to set the zoom scale to the maximum
pinchScale.value = zoomRange.max;
zoomScale.value = withSpring(zoomRange.max, SPRING_CONFIG, triggerScaleChangedEvent);
pinchScale.set(zoomRange.max);
zoomScale.set(withSpring(zoomRange.max, SPRING_CONFIG, triggerScaleChangedEvent));
} else {
// Otherwise, we just update the pinch scale offset
pinchScale.value = zoomScale.value;
pinchScale.set(zoomScale.get());
triggerScaleChangedEvent();
}
});
Expand Down
23 changes: 11 additions & 12 deletions src/components/MultiGestureCanvas/useTapGestures.ts
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
/* eslint-disable no-param-reassign */
import {useMemo} from 'react';
import {useCallback, useMemo} from 'react';
import type {TapGesture} from 'react-native-gesture-handler';
import {Gesture} from 'react-native-gesture-handler';
import {runOnJS, useWorkletCallback, withSpring} from 'react-native-reanimated';
import {runOnJS, withSpring} from 'react-native-reanimated';
import {DOUBLE_TAP_SCALE, SPRING_CONFIG} from './constants';
import type {MultiGestureCanvasVariables} from './types';
import * as MultiGestureCanvasUtils from './utils';
Expand Down Expand Up @@ -46,7 +46,7 @@ const useTapGestures = ({
// On double tap the content should be zoomed to fill, but at least zoomed by DOUBLE_TAP_SCALE
const doubleTapScale = useMemo(() => Math.max(DOUBLE_TAP_SCALE, maxContentScale / minContentScale), [maxContentScale, minContentScale]);

const zoomToCoordinates = useWorkletCallback(
const zoomToCoordinates = useCallback(
(focalX: number, focalY: number, callback: () => void) => {
'worklet';

Expand Down Expand Up @@ -111,19 +111,18 @@ const useTapGestures = ({
offsetAfterZooming.y = 0;
}

// eslint-disable-next-line react-compiler/react-compiler
offsetX.value = withSpring(offsetAfterZooming.x, SPRING_CONFIG);
offsetY.value = withSpring(offsetAfterZooming.y, SPRING_CONFIG);
zoomScale.value = withSpring(doubleTapScale, SPRING_CONFIG, callback);
pinchScale.value = doubleTapScale;
offsetX.set(withSpring(offsetAfterZooming.x, SPRING_CONFIG));
offsetY.set(withSpring(offsetAfterZooming.y, SPRING_CONFIG));
zoomScale.set(withSpring(doubleTapScale, SPRING_CONFIG, callback));
pinchScale.set(doubleTapScale);
},
[scaledContentWidth, scaledContentHeight, canvasSize, doubleTapScale],
[stopAnimation, canvasSize.width, canvasSize.height, scaledContentWidth, scaledContentHeight, doubleTapScale, offsetX, offsetY, zoomScale, pinchScale],
);

const doubleTapGesture = Gesture.Tap()
// The first argument is not used, but must be defined
.onTouchesDown((_evt, state) => {
if (!shouldDisableTransformationGestures.value) {
if (!shouldDisableTransformationGestures.get()) {
return;
}

Expand All @@ -137,13 +136,13 @@ const useTapGestures = ({
'worklet';

if (onScaleChanged != null) {
runOnJS(onScaleChanged)(zoomScale.value);
runOnJS(onScaleChanged)(zoomScale.get());
}
};

// If the content is already zoomed, we want to reset the zoom,
// otherwise we want to zoom in
if (zoomScale.value > 1) {
if (zoomScale.get() > 1) {
reset(true, triggerScaleChangedEvent);
} else {
zoomToCoordinates(evt.x, evt.y, triggerScaleChangedEvent);
Expand Down
19 changes: 7 additions & 12 deletions src/components/ReportActionItem/ReportPreview.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -135,7 +135,7 @@ function ReportPreview({
const iouSettled = ReportUtils.isSettled(iouReportID) || action?.childStatusNum === CONST.REPORT.STATUS_NUM.REIMBURSED;
const previewMessageOpacity = useSharedValue(1);
const previewMessageStyle = useAnimatedStyle(() => ({
opacity: previewMessageOpacity.value,
opacity: previewMessageOpacity.get(),
}));
const checkMarkScale = useSharedValue(iouSettled ? 1 : 0);

Expand Down Expand Up @@ -425,11 +425,11 @@ function ReportPreview({
return;
}

// eslint-disable-next-line react-compiler/react-compiler
previewMessageOpacity.value = withTiming(0.75, {duration: CONST.ANIMATION_PAID_DURATION / 2}, () => {
// eslint-disable-next-line react-compiler/react-compiler
previewMessageOpacity.value = withTiming(1, {duration: CONST.ANIMATION_PAID_DURATION / 2});
});
previewMessageOpacity.set(
withTiming(0.75, {duration: CONST.ANIMATION_PAID_DURATION / 2}, () => {
previewMessageOpacity.set(withTiming(1, {duration: CONST.ANIMATION_PAID_DURATION / 2}));
}),
);
// We only want to animate the text when the text changes
// eslint-disable-next-line react-compiler/react-compiler, react-hooks/exhaustive-deps
}, [previewMessage, previewMessageOpacity]);
Expand All @@ -439,12 +439,7 @@ function ReportPreview({
return;
}

if (isPaidAnimationRunning) {
// eslint-disable-next-line react-compiler/react-compiler
checkMarkScale.value = withDelay(CONST.ANIMATION_PAID_CHECKMARK_DELAY, withSpring(1, {duration: CONST.ANIMATION_PAID_DURATION}));
} else {
checkMarkScale.value = 1;
}
checkMarkScale.set(isPaidAnimationRunning ? withDelay(CONST.ANIMATION_PAID_CHECKMARK_DELAY, withSpring(1, {duration: CONST.ANIMATION_PAID_DURATION})) : 1);
}, [isPaidAnimationRunning, iouSettled, checkMarkScale]);

return (
Expand Down
44 changes: 22 additions & 22 deletions src/components/SettlementButton/AnimatedSettlementButton.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -23,20 +23,20 @@ function AnimatedSettlementButton({isPaidAnimationRunning, onAnimationFinish, is
const height = useSharedValue<number>(variables.componentSizeNormal);
const buttonMarginTop = useSharedValue<number>(styles.expenseAndReportPreviewTextButtonContainer.gap);
const buttonStyles = useAnimatedStyle(() => ({
transform: [{scale: buttonScale.value}],
opacity: buttonOpacity.value,
transform: [{scale: buttonScale.get()}],
opacity: buttonOpacity.get(),
}));
const paymentCompleteTextStyles = useAnimatedStyle(() => ({
transform: [{scale: paymentCompleteTextScale.value}],
opacity: paymentCompleteTextOpacity.value,
transform: [{scale: paymentCompleteTextScale.get()}],
opacity: paymentCompleteTextOpacity.get(),
position: 'absolute',
alignSelf: 'center',
}));
const containerStyles = useAnimatedStyle(() => ({
height: height.value,
height: height.get(),
justifyContent: 'center',
overflow: 'hidden',
marginTop: buttonMarginTop.value,
marginTop: buttonMarginTop.get(),
}));
const buttonDisabledStyle = isPaidAnimationRunning
? {
Expand All @@ -46,33 +46,33 @@ function AnimatedSettlementButton({isPaidAnimationRunning, onAnimationFinish, is
: undefined;

const resetAnimation = useCallback(() => {
// eslint-disable-next-line react-compiler/react-compiler
buttonScale.value = 1;
buttonOpacity.value = 1;
paymentCompleteTextScale.value = 0;
paymentCompleteTextOpacity.value = 1;
height.value = variables.componentSizeNormal;
buttonMarginTop.value = styles.expenseAndReportPreviewTextButtonContainer.gap;
buttonScale.set(1);
buttonOpacity.set(1);
paymentCompleteTextScale.set(0);
paymentCompleteTextOpacity.set(1);
height.set(variables.componentSizeNormal);
buttonMarginTop.set(styles.expenseAndReportPreviewTextButtonContainer.gap);
}, [buttonScale, buttonOpacity, paymentCompleteTextScale, paymentCompleteTextOpacity, height, buttonMarginTop, styles.expenseAndReportPreviewTextButtonContainer.gap]);

useEffect(() => {
if (!isPaidAnimationRunning) {
resetAnimation();
return;
}
// eslint-disable-next-line react-compiler/react-compiler
buttonScale.value = withTiming(0, {duration: CONST.ANIMATION_PAID_DURATION});
buttonOpacity.value = withTiming(0, {duration: CONST.ANIMATION_PAID_DURATION});
paymentCompleteTextScale.value = withTiming(1, {duration: CONST.ANIMATION_PAID_DURATION});
buttonScale.set(withTiming(0, {duration: CONST.ANIMATION_PAID_DURATION}));
buttonOpacity.set(withTiming(0, {duration: CONST.ANIMATION_PAID_DURATION}));
paymentCompleteTextScale.set(withTiming(1, {duration: CONST.ANIMATION_PAID_DURATION}));

// Wait for the above animation + 1s delay before hiding the component
const totalDelay = CONST.ANIMATION_PAID_DURATION + CONST.ANIMATION_PAID_BUTTON_HIDE_DELAY;
height.value = withDelay(
totalDelay,
withTiming(0, {duration: CONST.ANIMATION_PAID_DURATION}, () => runOnJS(onAnimationFinish)()),
height.set(
withDelay(
totalDelay,
withTiming(0, {duration: CONST.ANIMATION_PAID_DURATION}, () => runOnJS(onAnimationFinish)()),
),
);
buttonMarginTop.value = withDelay(totalDelay, withTiming(0, {duration: CONST.ANIMATION_PAID_DURATION}));
paymentCompleteTextOpacity.value = withDelay(totalDelay, withTiming(0, {duration: CONST.ANIMATION_PAID_DURATION}));
buttonMarginTop.set(withDelay(totalDelay, withTiming(0, {duration: CONST.ANIMATION_PAID_DURATION})));
paymentCompleteTextOpacity.set(withDelay(totalDelay, withTiming(0, {duration: CONST.ANIMATION_PAID_DURATION})));
}, [isPaidAnimationRunning, onAnimationFinish, buttonOpacity, buttonScale, height, paymentCompleteTextOpacity, paymentCompleteTextScale, buttonMarginTop, resetAnimation]);

return (
Expand Down
31 changes: 17 additions & 14 deletions src/components/SplashScreenHider/index.native.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -17,10 +17,10 @@ function SplashScreenHider({onHide = () => {}}: SplashScreenHiderProps): SplashS
const scale = useSharedValue(1);

const opacityStyle = useAnimatedStyle<ViewStyle>(() => ({
opacity: opacity.value,
opacity: opacity.get(),
}));
const scaleStyle = useAnimatedStyle<ViewStyle>(() => ({
transform: [{scale: scale.value}],
transform: [{scale: scale.get()}],
}));

const hideHasBeenCalled = useRef(false);
Expand All @@ -34,19 +34,22 @@ function SplashScreenHider({onHide = () => {}}: SplashScreenHiderProps): SplashS
hideHasBeenCalled.current = true;

BootSplash.hide().then(() => {
// eslint-disable-next-line react-compiler/react-compiler
scale.value = withTiming(0, {
duration: 200,
easing: Easing.back(2),
});
scale.set(
withTiming(0, {
duration: 200,
easing: Easing.back(2),
}),
);

opacity.value = withTiming(
0,
{
duration: 250,
easing: Easing.out(Easing.ease),
},
() => runOnJS(onHide)(),
opacity.set(
withTiming(
0,
{
duration: 250,
easing: Easing.out(Easing.ease),
},
() => runOnJS(onHide)(),
),
);
});
}, [opacity, scale, onHide]);
Expand Down
Loading

0 comments on commit 73c358f

Please sign in to comment.