810d167426
Reanimated 4 (New Architecture, #10980) rewrote spring termination: with `overshootClamping`, which drawer-layout always passes, an animation now ends as soon as the position leaves the corridor between its start and its target. Reanimated 3 only treated crossing the target as overshoot. So on Reanimated 4 any release whose velocity opposes the direction the drawer settles in ends the spring on its first frame, and the drawer teleports instead of animating. Two easy ways in: an aborted drag under `swipeMinDistance`/`swipeMinVelocity`, where `nextOpen` keeps its current value while the finger was moving the other way, and dragging the already-open drawer past its edge before releasing. Refresh the patch against upstream `main`, which fixes this by zeroing velocity that opposes the spring direction, and take the two other changes to the same function while we are here: an early return when the drawer is already at the target, and `useLayoutEffect` for the `open` prop so the follow-up `toggleDrawer` call does not depend on the spring still running to be deduped. Regenerated with `pnpm patch` / `pnpm patch-commit`. Also adds the patch doc this one was missing. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_011DRYvgEawpfehopEibJXQB
62 lines
2.2 KiB
Diff
62 lines
2.2 KiB
Diff
diff --git a/lib/module/views/Drawer.native.js b/lib/module/views/Drawer.native.js
|
|
index 38470a658878a63919186257041098747ec55473..1836d661ca0f8fa3e886da06e72eb004cd8f585e 100644
|
|
--- a/lib/module/views/Drawer.native.js
|
|
+++ b/lib/module/views/Drawer.native.js
|
|
@@ -124,17 +124,33 @@ export function Drawer({
|
|
}
|
|
onTransitionEnd?.(!open);
|
|
});
|
|
+ const animatingTo = useSharedValue(null)
|
|
const toggleDrawer = React.useCallback((open, velocity) => {
|
|
'worklet';
|
|
|
|
const translateX = getDrawerTranslationX(open);
|
|
+
|
|
+ if (translationX.value === translateX) {
|
|
+ return;
|
|
+ }
|
|
+
|
|
+ if (animatingTo.value === (open ? 'open' : 'close')) {
|
|
+ return;
|
|
+ }
|
|
+
|
|
if (velocity === undefined) {
|
|
runOnJS(onAnimationStart)(open);
|
|
}
|
|
touchStartX.value = 0;
|
|
touchX.value = 0;
|
|
+
|
|
+ // Ignore velocity that opposes the spring direction.
|
|
+ // Otherwise overshootClamping makes the spring snap instantly.
|
|
+ const isSameDirection = velocity !== undefined && (translateX > translationX.value && velocity > 0 || translateX < translationX.value && velocity < 0);
|
|
+ const effectiveVelocity = isSameDirection ? velocity : 0;
|
|
+ animatingTo.value = open ? 'open' : 'close';
|
|
translationX.value = withSpring(translateX, {
|
|
- velocity,
|
|
+ velocity: effectiveVelocity,
|
|
stiffness: 1000,
|
|
damping: 500,
|
|
mass: 3,
|
|
@@ -142,14 +158,19 @@ export function Drawer({
|
|
restDisplacementThreshold: 0.01,
|
|
restSpeedThreshold: 0.01,
|
|
reduceMotion: ReduceMotion.Never
|
|
- }, finished => runOnJS(onAnimationEnd)(open, finished));
|
|
+ }, finished => {
|
|
+ animatingTo.value = null;
|
|
+ runOnJS(onAnimationEnd)(open, finished);
|
|
+ });
|
|
if (open) {
|
|
runOnJS(onOpen)();
|
|
} else {
|
|
runOnJS(onClose)();
|
|
}
|
|
}, [getDrawerTranslationX, onAnimationEnd, onAnimationStart, onClose, onOpen, touchStartX, touchX, translationX]);
|
|
- React.useEffect(() => toggleDrawer(open), [open, toggleDrawer]);
|
|
+ React.useLayoutEffect(() => {
|
|
+ toggleDrawer(open);
|
|
+ }, [open, toggleDrawer]);
|
|
const startX = useSharedValue(0);
|
|
const pan = React.useMemo(() => {
|
|
let panGesture = Gesture?.Pan().onBegin(event => {
|