diff --git a/src/components/ReanimatedSwipeable.tsx b/src/components/ReanimatedSwipeable.tsx index 7714af0f23..08e241b101 100644 --- a/src/components/ReanimatedSwipeable.tsx +++ b/src/components/ReanimatedSwipeable.tsx @@ -18,15 +18,18 @@ import { import type { PanGestureHandlerProps } from '../handlers/PanGestureHandler'; import type { PanGestureHandlerEventPayload } from '../handlers/GestureHandlerEventPayload'; import Animated, { + ReduceMotion, SharedValue, interpolate, + measure, runOnJS, + runOnUI, + useAnimatedRef, useAnimatedStyle, useSharedValue, withSpring, } from 'react-native-reanimated'; import { - Dimensions, I18nManager, LayoutChangeEvent, StyleProp, @@ -257,20 +260,9 @@ const Swipeable = forwardRef( const leftWidth = useSharedValue(0); const rightWidth = useSharedValue(0); - // used for synchronizing layout measurements between JS and UI - const rightOffset = useSharedValue(null); - const showLeftProgress = useSharedValue(0); const showRightProgress = useSharedValue(0); - const updateRightElementWidth = useCallback(() => { - 'worklet'; - if (rightOffset.value === null) { - rightOffset.value = rowWidth.value; - } - rightWidth.value = Math.max(0, rowWidth.value - rightOffset.value); - }, [rightOffset, rightWidth, rowWidth]); - const updateAnimatedEvent = useCallback(() => { 'worklet'; @@ -372,11 +364,12 @@ const Swipeable = forwardRef( 'worklet'; const translationSpringConfig = { - duration: 1000, - dampingRatio: 0.9, - stiffness: 500, + mass: 2, + damping: 1000, + stiffness: 700, velocity: velocityX, overshootClamping: true, + reduceMotion: ReduceMotion.System, ...animationOptions, }; @@ -412,16 +405,17 @@ const Swipeable = forwardRef( } ); - const progressTarget = toValue === 0 ? 0 : 1; + const progressTarget = toValue === 0 ? 0 : 1 * Math.sign(toValue); - showLeftProgress.value = - showLeftProgress.value > 0 - ? withSpring(progressTarget, progressSpringConfig) - : 0; - showRightProgress.value = - showRightProgress.value > 0 - ? withSpring(progressTarget, progressSpringConfig) - : 0; + showLeftProgress.value = withSpring( + Math.max(progressTarget, 0), + progressSpringConfig + ); + + showRightProgress.value = withSpring( + Math.max(-progressTarget, 0), + progressSpringConfig + ); dispatchImmediateEvents(frozenRowState, toValue); @@ -440,20 +434,66 @@ const Swipeable = forwardRef( ] ); + const leftLayoutRef = useAnimatedRef(); + const leftWrapperLayoutRef = useAnimatedRef(); + const rightLayoutRef = useAnimatedRef(); + + const updateElementWidths = useCallback(() => { + 'worklet'; + const leftLayout = measure(leftLayoutRef); + const leftWrapperLayout = measure(leftWrapperLayoutRef); + const rightLayout = measure(rightLayoutRef); + leftWidth.value = + (leftLayout?.pageX ?? 0) - (leftWrapperLayout?.pageX ?? 0); + + rightWidth.value = + rowWidth.value - + (rightLayout?.pageX ?? rowWidth.value) + + (leftWrapperLayout?.pageX ?? 0); + }, [ + leftLayoutRef, + leftWrapperLayoutRef, + rightLayoutRef, + leftWidth, + rightWidth, + rowWidth.value, + ]); + const swipeableMethods = useMemo( () => ({ close() { 'worklet'; - animateRow(0); + if (_WORKLET) { + animateRow(0); + return; + } + runOnUI(() => { + animateRow(0); + })(); }, openLeft() { 'worklet'; - animateRow(leftWidth.value); + if (_WORKLET) { + updateElementWidths(); + animateRow(leftWidth.value); + return; + } + runOnUI(() => { + updateElementWidths(); + animateRow(leftWidth.value); + })(); }, openRight() { 'worklet'; - // rightOffset and rowWidth are already much sooner than rightWidth - animateRow((rightOffset.value ?? 0) - rowWidth.value); + if (_WORKLET) { + updateElementWidths(); + animateRow(-rightWidth.value); + return; + } + runOnUI(() => { + updateElementWidths(); + animateRow(-rightWidth.value); + })(); }, reset() { 'worklet'; @@ -464,14 +504,14 @@ const Swipeable = forwardRef( }, }), [ + animateRow, + updateElementWidths, leftWidth, - rightOffset, - rowWidth, + rightWidth, userDrag, showLeftProgress, appliedTranslation, rowState, - animateRow, ] ); @@ -484,38 +524,31 @@ const Swipeable = forwardRef( // As stated in `Dimensions.get` docstring, this function should be called on every render // since dimensions may change (e.g. orientation change) - const hiddenSwipeableOffset = Dimensions.get('window').width + 1; const leftActionAnimation = useAnimatedStyle(() => { return { - transform: [ - { - translateX: - showLeftProgress.value === 0 ? -hiddenSwipeableOffset : 0, - }, - ], + opacity: showLeftProgress.value === 0 ? 0 : 1, }; }); const leftElement = useCallback( () => ( - + {renderLeftActions?.( showLeftProgress, appliedTranslation, swipeableMethods )} - - (leftWidth.value = nativeEvent.layout.x) - } - /> + ), [ appliedTranslation, leftActionAnimation, - leftWidth, + leftLayoutRef, + leftWrapperLayoutRef, renderLeftActions, showLeftProgress, swipeableMethods, @@ -524,12 +557,7 @@ const Swipeable = forwardRef( const rightActionAnimation = useAnimatedStyle(() => { return { - transform: [ - { - translateX: - showRightProgress.value === 0 ? hiddenSwipeableOffset : 0, - }, - ], + opacity: showRightProgress.value === 0 ? 0 : 1, }; }); @@ -541,18 +569,14 @@ const Swipeable = forwardRef( appliedTranslation, swipeableMethods )} - { - rightOffset.value = nativeEvent.layout.x; - }} - /> + ), [ appliedTranslation, renderRightActions, rightActionAnimation, - rightOffset, + rightLayoutRef, showRightProgress, swipeableMethods, ] @@ -564,8 +588,6 @@ const Swipeable = forwardRef( const { velocityX } = event; userDrag.value = event.translationX; - updateRightElementWidth(); - const leftThresholdProp = leftThreshold ?? leftWidth.value / 2; const rightThresholdProp = rightThreshold ?? rightWidth.value / 2; @@ -603,7 +625,6 @@ const Swipeable = forwardRef( rightWidth, rowState, userDrag, - updateRightElementWidth, ] ); @@ -632,9 +653,7 @@ const Swipeable = forwardRef( .enabled(enabled !== false) .enableTrackpadTwoFingerGesture(enableTrackpadTwoFingerGesture) .activeOffsetX([-dragOffsetFromRightEdge, dragOffsetFromLeftEdge]) - .onStart(() => { - updateRightElementWidth(); - }) + .onStart(updateElementWidths) .onUpdate( (event: GestureUpdateEvent) => { userDrag.value = event.translationX; @@ -679,7 +698,7 @@ const Swipeable = forwardRef( onSwipeableOpenStartDrag, rowState, updateAnimatedEvent, - updateRightElementWidth, + updateElementWidths, userDrag, ] );