Skip to content

Commit

Permalink
Continue updating reanimated usage
Browse files Browse the repository at this point in the history
  • Loading branch information
blazejkustra committed Nov 5, 2024
1 parent 4ac36b1 commit 0667cf7
Show file tree
Hide file tree
Showing 4 changed files with 157 additions and 125 deletions.
26 changes: 18 additions & 8 deletions src/components/AvatarCropModal/AvatarCropModal.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ import type {LayoutChangeEvent} from 'react-native';
import {Gesture, GestureHandlerRootView} from 'react-native-gesture-handler';
import type {GestureUpdateEvent, PanGestureChangeEventPayload, PanGestureHandlerEventPayload} from 'react-native-gesture-handler';
import ImageSize from 'react-native-image-size';
import {interpolate, runOnUI, useSharedValue, useWorkletCallback} from 'react-native-reanimated';
import {interpolate, runOnUI, useSharedValue} from 'react-native-reanimated';
import Button from '@components/Button';
import HeaderGap from '@components/HeaderGap';
import HeaderWithBackButton from '@components/HeaderWithBackButton';
Expand Down Expand Up @@ -143,12 +143,18 @@ function AvatarCropModal({imageUri = '', imageName = '', imageType = '', onClose
/**
* Validates that value is within the provided mix/max range.
*/
const clamp = useWorkletCallback((value: number, [min, max]) => interpolate(value, [min, max], [min, max], 'clamp'), []);
const clamp = useCallback((value: number, [min, max]: [number, number]) => {
'worklet';

return interpolate(value, [min, max], [min, max], 'clamp');
}, []);

/**
* Returns current image size taking into account scale and rotation.
*/
const getDisplayedImageSize = useWorkletCallback(() => {
const getDisplayedImageSize = useCallback(() => {
'worklet';

let height = imageContainerSize * scale.get();
let width = imageContainerSize * scale.get();

Expand All @@ -161,13 +167,15 @@ function AvatarCropModal({imageUri = '', imageName = '', imageType = '', onClose
}

return {height, width};
}, [imageContainerSize, scale]);
}, [imageContainerSize, originalImageHeight, originalImageWidth, scale]);

/**
* Validates the offset to prevent overflow, and updates the image offset.
*/
const updateImageOffset = useWorkletCallback(
const updateImageOffset = useCallback(
(offsetX: number, offsetY: number) => {
'worklet';

const {height, width} = getDisplayedImageSize();
const maxOffsetX = (width - imageContainerSize) / 2;
const maxOffsetY = (height - imageContainerSize) / 2;
Expand All @@ -176,13 +184,15 @@ function AvatarCropModal({imageUri = '', imageName = '', imageType = '', onClose
prevMaxOffsetX.set(maxOffsetX);
prevMaxOffsetY.set(maxOffsetY);
},
[imageContainerSize, scale, clamp],
[getDisplayedImageSize, imageContainerSize, translateX, clamp, translateY, prevMaxOffsetX, prevMaxOffsetY],
);

const newScaleValue = useWorkletCallback((newSliderValue: number, containerSize: number) => {
const newScaleValue = useCallback((newSliderValue: number, containerSize: number) => {
'worklet';

const {MAX_SCALE, MIN_SCALE} = CONST.AVATAR_CROP_MODAL;
return (newSliderValue / containerSize) * (MAX_SCALE - MIN_SCALE) + MIN_SCALE;
});
}, []);

/**
* Calculates new x & y image translate value on image panning
Expand Down
14 changes: 7 additions & 7 deletions src/components/CustomStatusBarAndBackground/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -44,14 +44,14 @@ function CustomStatusBarAndBackground({isNested = false}: CustomStatusBarAndBack
const statusBarAnimation = useSharedValue(0);

useAnimatedReaction(
() => statusBarAnimation.value,
() => statusBarAnimation.get(),
(current, previous) => {
// Do not run if either of the animated value is null
// or previous animated value is greater than or equal to the current one
if (previous === null || current === null || current <= previous) {
return;
}
const backgroundColor = interpolateColor(statusBarAnimation.value, [0, 1], [prevStatusBarBackgroundColor.value, statusBarBackgroundColor.value]);
const backgroundColor = interpolateColor(statusBarAnimation.get(), [0, 1], [prevStatusBarBackgroundColor.get(), statusBarBackgroundColor.get()]);
runOnJS(updateStatusBarAppearance)({backgroundColor});
},
);
Expand Down Expand Up @@ -92,17 +92,17 @@ function CustomStatusBarAndBackground({isNested = false}: CustomStatusBarAndBack
currentScreenBackgroundColor = backgroundColorFromRoute || pageTheme.backgroundColor;
}

prevStatusBarBackgroundColor.value = statusBarBackgroundColor.value;
statusBarBackgroundColor.value = currentScreenBackgroundColor;
prevStatusBarBackgroundColor.set(statusBarBackgroundColor.get());
statusBarBackgroundColor.set(currentScreenBackgroundColor);

const callUpdateStatusBarAppearance = () => {
updateStatusBarAppearance({statusBarStyle: newStatusBarStyle});
setStatusBarStyle(newStatusBarStyle);
};

const callUpdateStatusBarBackgroundColor = () => {
statusBarAnimation.value = 0;
statusBarAnimation.value = withDelay(300, withTiming(1));
statusBarAnimation.set(0);
statusBarAnimation.set(withDelay(300, withTiming(1)));
};

// Don't update the status bar style if it's the same as the current one, to prevent flashing.
Expand All @@ -121,7 +121,7 @@ function CustomStatusBarAndBackground({isNested = false}: CustomStatusBarAndBack
callUpdateStatusBarAppearance();
}

if (currentScreenBackgroundColor !== theme.appBG || prevStatusBarBackgroundColor.value !== theme.appBG) {
if (currentScreenBackgroundColor !== theme.appBG || prevStatusBarBackgroundColor.get() !== theme.appBG) {
callUpdateStatusBarBackgroundColor();
}
},
Expand Down
84 changes: 46 additions & 38 deletions src/components/MultiGestureCanvas/index.tsx
Original file line number Diff line number Diff line change
@@ -1,12 +1,12 @@
import type {ForwardedRef} from 'react';
import React, {useEffect, useMemo, useRef} from 'react';
import React, {useCallback, useEffect, useMemo, useRef} from 'react';
import {View} from 'react-native';
import type {GestureType} from 'react-native-gesture-handler';
import {Gesture, GestureDetector} from 'react-native-gesture-handler';
import type {GestureRef} from 'react-native-gesture-handler/lib/typescript/handlers/gestures/gesture';
import type PagerView from 'react-native-pager-view';
import type {SharedValue} from 'react-native-reanimated';
import Animated, {cancelAnimation, runOnUI, useAnimatedStyle, useDerivedValue, useSharedValue, useWorkletCallback, withSpring} from 'react-native-reanimated';
import Animated, {cancelAnimation, runOnUI, useAnimatedStyle, useDerivedValue, useSharedValue, withSpring} from 'react-native-reanimated';
import useStyleUtils from '@hooks/useStyleUtils';
import useThemeStyles from '@hooks/useThemeStyles';
import type ChildrenProps from '@src/types/utils/ChildrenProps';
Expand Down Expand Up @@ -92,7 +92,7 @@ function MultiGestureCanvas({
// Adding together zoom scale and the initial scale to fit the content into the canvas
// Using the minimum content scale, so that the image is not bigger than the canvas
// and not smaller than needed to fit
const totalScale = useDerivedValue(() => zoomScale.value * minContentScale, [minContentScale]);
const totalScale = useDerivedValue(() => zoomScale.get() * minContentScale, [minContentScale]);

const panTranslateX = useSharedValue(0);
const panTranslateY = useSharedValue(0);
Expand All @@ -110,44 +110,50 @@ function MultiGestureCanvas({
/**
* Stops any currently running decay animation from panning
*/
const stopAnimation = useWorkletCallback(() => {
const stopAnimation = useCallback(() => {
'worklet';

cancelAnimation(offsetX);
cancelAnimation(offsetY);
});
}, [offsetX, offsetY]);

/**
* Resets the canvas to the initial state and animates back smoothly
*/
const reset = useWorkletCallback((animated: boolean, callback?: () => void) => {
stopAnimation();

// eslint-disable-next-line react-compiler/react-compiler
offsetX.value = 0;
offsetY.value = 0;
pinchScale.value = 1;

if (animated) {
panTranslateX.value = withSpring(0, SPRING_CONFIG);
panTranslateY.value = withSpring(0, SPRING_CONFIG);
pinchTranslateX.value = withSpring(0, SPRING_CONFIG);
pinchTranslateY.value = withSpring(0, SPRING_CONFIG);
zoomScale.value = withSpring(1, SPRING_CONFIG, callback);

return;
}

panTranslateX.value = 0;
panTranslateY.value = 0;
pinchTranslateX.value = 0;
pinchTranslateY.value = 0;
zoomScale.value = 1;

if (callback === undefined) {
return;
}

callback();
});
const reset = useCallback(
(animated: boolean, callback?: () => void) => {
'worklet';

stopAnimation();

offsetX.set(0);
offsetY.set(0);
pinchScale.set(1);

if (animated) {
panTranslateX.set(withSpring(0, SPRING_CONFIG));
panTranslateY.set(withSpring(0, SPRING_CONFIG));
pinchTranslateX.set(withSpring(0, SPRING_CONFIG));
pinchTranslateY.set(withSpring(0, SPRING_CONFIG));
zoomScale.set(withSpring(1, SPRING_CONFIG, callback));

return;
}

panTranslateX.set(0);
panTranslateY.set(0);
pinchTranslateX.set(0);
pinchTranslateY.set(0);
zoomScale.set(1);

if (callback === undefined) {
return;
}

callback();
},
[offsetX, offsetY, panTranslateX, panTranslateY, pinchScale, pinchTranslateX, pinchTranslateY, stopAnimation, zoomScale],
);

const {singleTapGesture: baseSingleTapGesture, doubleTapGesture} = useTapGestures({
canvasSize,
Expand All @@ -164,6 +170,7 @@ function MultiGestureCanvas({
onTap,
shouldDisableTransformationGestures,
});
// eslint-disable-next-line react-compiler/react-compiler
const singleTapGesture = baseSingleTapGesture.requireExternalGestureToFail(doubleTapGesture, panGestureRef);

const panGestureSimultaneousList = useMemo(
Expand All @@ -186,6 +193,7 @@ function MultiGestureCanvas({
onSwipeDown,
})
.simultaneousWithExternalGesture(...panGestureSimultaneousList)
// eslint-disable-next-line react-compiler/react-compiler
.withRef(panGestureRef);

const pinchGesture = usePinchGesture({
Expand Down Expand Up @@ -217,8 +225,8 @@ function MultiGestureCanvas({

// Animate the x and y position of the content within the canvas based on all of the gestures
const animatedStyles = useAnimatedStyle(() => {
const x = pinchTranslateX.value + panTranslateX.value + offsetX.value;
const y = pinchTranslateY.value + panTranslateY.value + offsetY.value;
const x = pinchTranslateX.get() + panTranslateX.get() + offsetX.get();
const y = pinchTranslateY.get() + panTranslateY.get() + offsetY.get();

return {
transform: [
Expand All @@ -228,7 +236,7 @@ function MultiGestureCanvas({
{
translateY: y,
},
{scale: totalScale.value},
{scale: totalScale.get()},
],
// Hide the image if the size is not ready yet
opacity: contentSizeProp?.width ? 1 : 0,
Expand Down
Loading

0 comments on commit 0667cf7

Please sign in to comment.