Skip to content

Commit 8351e4b

Browse files
authored
[ReanimatedSwipeable] Multiple bug fixes and improvements (#3149)
## Description This PR applies multiple bug fixes and improvements to the `ReanimatedSwipeable` ### Changes: - Fix reversed swipe direction being supplied to `open` and `willOpen` callbacks. - Fix `startDrag` callbacks receiving invalid `direction` when opening swipeable. - Fix unpredictable swipe direction being supplied to `close` and `willClose` callbacks. - Fix `onSwipeableWillClose` and `onSwipeableClose` callbacks sometimes being called on open. - Fix `progress` value skipping last `10%` of it's range and having a delay relative to the `translation`. closes #3147 ## Test plan Will create an example showcasing outputs of all the affected callbacks, work in progress.
1 parent 6f7b7ea commit 8351e4b

File tree

1 file changed

+68
-45
lines changed

1 file changed

+68
-45
lines changed

src/components/ReanimatedSwipeable.tsx

Lines changed: 68 additions & 45 deletions
Original file line numberDiff line numberDiff line change
@@ -41,6 +41,11 @@ type SwipeableExcludes = Exclude<
4141
'onGestureEvent' | 'onHandlerStateChange'
4242
>;
4343

44+
enum SwipeDirection {
45+
LEFT = 'left',
46+
RIGHT = 'right',
47+
}
48+
4449
export interface SwipeableProps
4550
extends Pick<PanGestureHandlerProps, SwipeableExcludes> {
4651
/**
@@ -110,37 +115,45 @@ export interface SwipeableProps
110115
* Called when action panel gets open (either right or left).
111116
*/
112117
onSwipeableOpen?: (
113-
direction: 'left' | 'right',
118+
direction: SwipeDirection.LEFT | SwipeDirection.RIGHT,
114119
swipeable: SwipeableMethods
115120
) => void;
116121

117122
/**
118123
* Called when action panel is closed.
119124
*/
120125
onSwipeableClose?: (
121-
direction: 'left' | 'right',
126+
direction: SwipeDirection.LEFT | SwipeDirection.RIGHT,
122127
swipeable: SwipeableMethods
123128
) => void;
124129

125130
/**
126131
* Called when action panel starts animating on open (either right or left).
127132
*/
128-
onSwipeableWillOpen?: (direction: 'left' | 'right') => void;
133+
onSwipeableWillOpen?: (
134+
direction: SwipeDirection.LEFT | SwipeDirection.RIGHT
135+
) => void;
129136

130137
/**
131138
* Called when action panel starts animating on close.
132139
*/
133-
onSwipeableWillClose?: (direction: 'left' | 'right') => void;
140+
onSwipeableWillClose?: (
141+
direction: SwipeDirection.LEFT | SwipeDirection.RIGHT
142+
) => void;
134143

135144
/**
136145
* Called when action panel starts being shown on dragging to open.
137146
*/
138-
onSwipeableOpenStartDrag?: (direction: 'left' | 'right') => void;
147+
onSwipeableOpenStartDrag?: (
148+
direction: SwipeDirection.LEFT | SwipeDirection.RIGHT
149+
) => void;
139150

140151
/**
141152
* Called when action panel starts being shown on dragging to close.
142153
*/
143-
onSwipeableCloseStartDrag?: (direction: 'left' | 'right') => void;
154+
onSwipeableCloseStartDrag?: (
155+
direction: SwipeDirection.LEFT | SwipeDirection.RIGHT
156+
) => void;
144157

145158
/**
146159
* `progress`: Equals `0` when `swipeable` is closed, `1` when `swipeable` is opened.
@@ -270,16 +283,6 @@ const Swipeable = forwardRef<SwipeableMethods, SwipeableProps>(
270283
rightWidth.value = Math.max(0, rowWidth.value - rightOffset.value);
271284
};
272285

273-
const calculateCurrentOffset = useCallback(() => {
274-
'worklet';
275-
updateRightElementWidth();
276-
return rowState.value === 1
277-
? leftWidth.value
278-
: rowState.value === -1
279-
? -rowWidth.value - rightOffset.value!
280-
: 0;
281-
}, [leftWidth, rightOffset, rowState, rowWidth]);
282-
283286
const updateAnimatedEvent = () => {
284287
'worklet';
285288

@@ -334,27 +337,30 @@ const Swipeable = forwardRef<SwipeableMethods, SwipeableProps>(
334337

335338
const dispatchImmediateEvents = useCallback(
336339
(fromValue: number, toValue: number) => {
337-
if (toValue > 0 && onSwipeableWillOpen) {
338-
onSwipeableWillOpen('left');
339-
} else if (toValue < 0 && onSwipeableWillOpen) {
340-
onSwipeableWillOpen('right');
341-
} else if (onSwipeableWillClose) {
342-
const closingDirection = fromValue > 0 ? 'left' : 'right';
343-
onSwipeableWillClose(closingDirection);
340+
if (toValue > 0) {
341+
onSwipeableWillOpen?.(SwipeDirection.RIGHT);
342+
} else if (toValue < 0) {
343+
onSwipeableWillOpen?.(SwipeDirection.LEFT);
344+
} else {
345+
onSwipeableWillClose?.(
346+
fromValue > 0 ? SwipeDirection.LEFT : SwipeDirection.RIGHT
347+
);
344348
}
345349
},
346350
[onSwipeableWillClose, onSwipeableWillOpen]
347351
);
348352

349353
const dispatchEndEvents = useCallback(
350354
(fromValue: number, toValue: number) => {
351-
if (toValue > 0 && onSwipeableOpen) {
352-
onSwipeableOpen('left', swipeableMethods.current);
353-
} else if (toValue < 0 && onSwipeableOpen) {
354-
onSwipeableOpen('right', swipeableMethods.current);
355-
} else if (onSwipeableClose) {
356-
const closingDirection = fromValue > 0 ? 'left' : 'right';
357-
onSwipeableClose(closingDirection, swipeableMethods.current);
355+
if (toValue > 0) {
356+
onSwipeableOpen?.(SwipeDirection.RIGHT, swipeableMethods.current);
357+
} else if (toValue < 0) {
358+
onSwipeableOpen?.(SwipeDirection.LEFT, swipeableMethods.current);
359+
} else {
360+
onSwipeableClose?.(
361+
fromValue > 0 ? SwipeDirection.LEFT : SwipeDirection.RIGHT,
362+
swipeableMethods.current
363+
);
358364
}
359365
},
360366
[onSwipeableClose, onSwipeableOpen]
@@ -363,9 +369,8 @@ const Swipeable = forwardRef<SwipeableMethods, SwipeableProps>(
363369
const animationOptionsProp = animationOptions;
364370

365371
const animateRow = useCallback(
366-
(fromValue: number, toValue: number, velocityX?: number) => {
372+
(toValue: number, velocityX?: number) => {
367373
'worklet';
368-
rowState.value = Math.sign(toValue);
369374

370375
const translationSpringConfig = {
371376
duration: 1000,
@@ -376,17 +381,34 @@ const Swipeable = forwardRef<SwipeableMethods, SwipeableProps>(
376381
...animationOptionsProp,
377382
};
378383

384+
const isClosing = toValue === 0;
385+
const moveToRight = isClosing ? rowState.value < 0 : toValue > 0;
386+
387+
const usedWidth = isClosing
388+
? moveToRight
389+
? rightWidth.value
390+
: leftWidth.value
391+
: moveToRight
392+
? leftWidth.value
393+
: rightWidth.value;
394+
379395
const progressSpringConfig = {
380396
...translationSpringConfig,
381-
velocity: 0,
397+
restDisplacementThreshold: 0.01,
398+
restSpeedThreshold: 0.01,
399+
velocity:
400+
velocityX &&
401+
interpolate(velocityX, [-usedWidth, usedWidth], [-1, 1]),
382402
};
383403

404+
const frozenRowState = rowState.value;
405+
384406
appliedTranslation.value = withSpring(
385407
toValue,
386408
translationSpringConfig,
387409
(isFinished) => {
388410
if (isFinished) {
389-
runOnJS(dispatchEndEvents)(fromValue, toValue);
411+
runOnJS(dispatchEndEvents)(frozenRowState, toValue);
390412
}
391413
}
392414
);
@@ -402,7 +424,9 @@ const Swipeable = forwardRef<SwipeableMethods, SwipeableProps>(
402424
? withSpring(progressTarget, progressSpringConfig)
403425
: 0;
404426

405-
runOnJS(dispatchImmediateEvents)(fromValue, toValue);
427+
runOnJS(dispatchImmediateEvents)(frozenRowState, toValue);
428+
429+
rowState.value = Math.sign(toValue);
406430
},
407431
[
408432
rowState,
@@ -432,15 +456,15 @@ const Swipeable = forwardRef<SwipeableMethods, SwipeableProps>(
432456
swipeableMethods.current = {
433457
close() {
434458
'worklet';
435-
animateRow(calculateCurrentOffset(), 0);
459+
animateRow(0);
436460
},
437461
openLeft() {
438462
'worklet';
439-
animateRow(calculateCurrentOffset(), leftWidth.value);
463+
animateRow(leftWidth.value);
440464
},
441465
openRight() {
442466
'worklet';
443-
animateRow(calculateCurrentOffset(), -rightWidth.value);
467+
animateRow(-rightWidth.value);
444468
},
445469
reset() {
446470
'worklet';
@@ -496,7 +520,6 @@ const Swipeable = forwardRef<SwipeableMethods, SwipeableProps>(
496520
const leftThreshold = leftThresholdProp ?? leftWidth.value / 2;
497521
const rightThreshold = rightThresholdProp ?? rightWidth.value / 2;
498522

499-
const startOffsetX = calculateCurrentOffset() + userDrag.value / friction;
500523
const translationX = (userDrag.value + DRAG_TOSS * velocityX) / friction;
501524

502525
let toValue = 0;
@@ -519,12 +542,12 @@ const Swipeable = forwardRef<SwipeableMethods, SwipeableProps>(
519542
}
520543
}
521544

522-
animateRow(startOffsetX, toValue, velocityX / friction);
545+
animateRow(toValue, velocityX / friction);
523546
};
524547

525548
const close = () => {
526549
'worklet';
527-
animateRow(calculateCurrentOffset(), 0);
550+
animateRow(0);
528551
};
529552

530553
const tapGesture = Gesture.Tap().onStart(() => {
@@ -541,12 +564,12 @@ const Swipeable = forwardRef<SwipeableMethods, SwipeableProps>(
541564

542565
const direction =
543566
rowState.value === -1
544-
? 'right'
567+
? SwipeDirection.RIGHT
545568
: rowState.value === 1
546-
? 'left'
569+
? SwipeDirection.LEFT
547570
: event.translationX > 0
548-
? 'left'
549-
: 'right';
571+
? SwipeDirection.RIGHT
572+
: SwipeDirection.LEFT;
550573

551574
if (!dragStarted.value) {
552575
dragStarted.value = true;

0 commit comments

Comments
 (0)