-
-
Notifications
You must be signed in to change notification settings - Fork 980
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
[ReanimatedSwipeable
] Multiple bug fixes and improvements
#3149
Changes from 17 commits
2b93465
866425d
bc54f51
2358efc
3a9088d
ae9f3a7
224468c
040c6f1
9366060
dfe58de
933338f
efc08b4
d7ebdce
61d521c
5284f21
d5dcab8
df4b647
a329a6d
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -111,37 +111,45 @@ export interface SwipeableProps | |
* Called when action panel gets open (either right or left). | ||
*/ | ||
onSwipeableOpen?: ( | ||
direction: 'left' | 'right', | ||
direction: SwipeDirection.LEFT | SwipeDirection.RIGHT, | ||
swipeable: SwipeableMethods | ||
) => void; | ||
|
||
/** | ||
* Called when action panel is closed. | ||
*/ | ||
onSwipeableClose?: ( | ||
direction: 'left' | 'right', | ||
direction: SwipeDirection.LEFT | SwipeDirection.RIGHT, | ||
swipeable: SwipeableMethods | ||
) => void; | ||
|
||
/** | ||
* Called when action panel starts animating on open (either right or left). | ||
*/ | ||
onSwipeableWillOpen?: (direction: 'left' | 'right') => void; | ||
onSwipeableWillOpen?: ( | ||
direction: SwipeDirection.LEFT | SwipeDirection.RIGHT | ||
) => void; | ||
|
||
/** | ||
* Called when action panel starts animating on close. | ||
*/ | ||
onSwipeableWillClose?: (direction: 'left' | 'right') => void; | ||
onSwipeableWillClose?: ( | ||
direction: SwipeDirection.LEFT | SwipeDirection.RIGHT | ||
) => void; | ||
|
||
/** | ||
* Called when action panel starts being shown on dragging to open. | ||
*/ | ||
onSwipeableOpenStartDrag?: (direction: 'left' | 'right') => void; | ||
onSwipeableOpenStartDrag?: ( | ||
direction: SwipeDirection.LEFT | SwipeDirection.RIGHT | ||
) => void; | ||
|
||
/** | ||
* Called when action panel starts being shown on dragging to close. | ||
*/ | ||
onSwipeableCloseStartDrag?: (direction: 'left' | 'right') => void; | ||
onSwipeableCloseStartDrag?: ( | ||
direction: SwipeDirection.LEFT | SwipeDirection.RIGHT | ||
) => void; | ||
|
||
/** | ||
* `progress`: Equals `0` when `swipeable` is closed, `1` when `swipeable` is opened. | ||
|
@@ -197,6 +205,11 @@ export interface SwipeableMethods { | |
reset: () => void; | ||
} | ||
|
||
enum SwipeDirection { | ||
LEFT = 'left', | ||
RIGHT = 'right', | ||
} | ||
|
||
const Swipeable = forwardRef<SwipeableMethods, SwipeableProps>( | ||
function Swipeable( | ||
props: SwipeableProps, | ||
|
@@ -274,16 +287,6 @@ const Swipeable = forwardRef<SwipeableMethods, SwipeableProps>( | |
rightWidth.value = Math.max(0, rowWidth.value - rightOffset.value); | ||
}; | ||
|
||
const calculateCurrentOffset = useCallback(() => { | ||
'worklet'; | ||
updateRightElementWidth(); | ||
return rowState.value === 1 | ||
? leftWidth.value | ||
: rowState.value === -1 | ||
? -rowWidth.value - rightOffset.value! | ||
: 0; | ||
}, [leftWidth, rightOffset, rowState, rowWidth]); | ||
|
||
const updateAnimatedEvent = () => { | ||
'worklet'; | ||
|
||
|
@@ -349,27 +352,30 @@ const Swipeable = forwardRef<SwipeableMethods, SwipeableProps>( | |
|
||
const dispatchImmediateEvents = useCallback( | ||
(fromValue: number, toValue: number) => { | ||
if (toValue > 0 && onSwipeableWillOpen) { | ||
onSwipeableWillOpen('left'); | ||
} else if (toValue < 0 && onSwipeableWillOpen) { | ||
onSwipeableWillOpen('right'); | ||
} else if (onSwipeableWillClose) { | ||
const closingDirection = fromValue > 0 ? 'left' : 'right'; | ||
onSwipeableWillClose(closingDirection); | ||
if (toValue > 0) { | ||
onSwipeableWillOpen?.(SwipeDirection.RIGHT); | ||
} else if (toValue < 0) { | ||
onSwipeableWillOpen?.(SwipeDirection.LEFT); | ||
} else { | ||
onSwipeableWillClose?.( | ||
fromValue > 0 ? SwipeDirection.LEFT : SwipeDirection.RIGHT | ||
); | ||
} | ||
}, | ||
[onSwipeableWillClose, onSwipeableWillOpen] | ||
); | ||
|
||
const dispatchEndEvents = useCallback( | ||
(fromValue: number, toValue: number) => { | ||
if (toValue > 0 && onSwipeableOpen) { | ||
onSwipeableOpen('left', swipeableMethods.current); | ||
} else if (toValue < 0 && onSwipeableOpen) { | ||
onSwipeableOpen('right', swipeableMethods.current); | ||
} else if (onSwipeableClose) { | ||
const closingDirection = fromValue > 0 ? 'left' : 'right'; | ||
onSwipeableClose(closingDirection, swipeableMethods.current); | ||
if (toValue > 0) { | ||
onSwipeableOpen?.(SwipeDirection.RIGHT, swipeableMethods.current); | ||
} else if (toValue < 0) { | ||
onSwipeableOpen?.(SwipeDirection.LEFT, swipeableMethods.current); | ||
} else { | ||
onSwipeableClose?.( | ||
fromValue > 0 ? SwipeDirection.LEFT : SwipeDirection.RIGHT, | ||
swipeableMethods.current | ||
); | ||
} | ||
}, | ||
[onSwipeableClose, onSwipeableOpen] | ||
|
@@ -378,9 +384,8 @@ const Swipeable = forwardRef<SwipeableMethods, SwipeableProps>( | |
const animationOptionsProp = animationOptions; | ||
|
||
const animateRow = useCallback( | ||
(fromValue: number, toValue: number, velocityX?: number) => { | ||
(toValue: number, velocityX?: number) => { | ||
'worklet'; | ||
rowState.value = Math.sign(toValue); | ||
|
||
const translationSpringConfig = { | ||
duration: 1000, | ||
|
@@ -391,17 +396,34 @@ const Swipeable = forwardRef<SwipeableMethods, SwipeableProps>( | |
...animationOptionsProp, | ||
}; | ||
|
||
const isClosing = toValue === 0; | ||
const moveToRight = isClosing ? rowState.value < 0 : toValue > 0; | ||
|
||
const usedWidth = isClosing | ||
? moveToRight | ||
? rightWidth.value | ||
: leftWidth.value | ||
: moveToRight | ||
? leftWidth.value | ||
: rightWidth.value; | ||
|
||
const progressSpringConfig = { | ||
...translationSpringConfig, | ||
velocity: 0, | ||
restDisplacementThreshold: 0.01, | ||
restSpeedThreshold: 0.01, | ||
velocity: | ||
velocityX && | ||
interpolate(velocityX, [-usedWidth, usedWidth], [-1, 1]), | ||
}; | ||
|
||
const frozenRowState = rowState.value; | ||
|
||
appliedTranslation.value = withSpring( | ||
toValue, | ||
translationSpringConfig, | ||
(isFinished) => { | ||
if (isFinished) { | ||
runOnJS(dispatchEndEvents)(fromValue, toValue); | ||
runOnJS(dispatchEndEvents)(frozenRowState, toValue); | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Are all events executed on the JS thread? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I believe all of them are, couldn't find any that aren't There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I think we can update the calling logic to check whether the callback is a worklet and run it on the UI thread if it is (that's for another PR though). There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Great idea, will do that. |
||
} | ||
} | ||
); | ||
|
@@ -417,7 +439,9 @@ const Swipeable = forwardRef<SwipeableMethods, SwipeableProps>( | |
? withSpring(progressTarget, progressSpringConfig) | ||
: 0; | ||
|
||
runOnJS(dispatchImmediateEvents)(fromValue, toValue); | ||
runOnJS(dispatchImmediateEvents)(frozenRowState, toValue); | ||
|
||
rowState.value = Math.sign(toValue); | ||
}, | ||
[ | ||
rowState, | ||
|
@@ -447,15 +471,15 @@ const Swipeable = forwardRef<SwipeableMethods, SwipeableProps>( | |
swipeableMethods.current = { | ||
close() { | ||
'worklet'; | ||
animateRow(calculateCurrentOffset(), 0); | ||
animateRow(0); | ||
}, | ||
openLeft() { | ||
'worklet'; | ||
animateRow(calculateCurrentOffset(), leftWidth.value); | ||
animateRow(leftWidth.value); | ||
}, | ||
openRight() { | ||
'worklet'; | ||
animateRow(calculateCurrentOffset(), -rightWidth.value); | ||
animateRow(-rightWidth.value); | ||
}, | ||
reset() { | ||
'worklet'; | ||
|
@@ -533,7 +557,6 @@ const Swipeable = forwardRef<SwipeableMethods, SwipeableProps>( | |
const leftThreshold = leftThresholdProp ?? leftWidth.value / 2; | ||
const rightThreshold = rightThresholdProp ?? rightWidth.value / 2; | ||
|
||
const startOffsetX = calculateCurrentOffset() + userDrag.value / friction; | ||
const translationX = (userDrag.value + DRAG_TOSS * velocityX) / friction; | ||
|
||
let toValue = 0; | ||
|
@@ -556,12 +579,12 @@ const Swipeable = forwardRef<SwipeableMethods, SwipeableProps>( | |
} | ||
} | ||
|
||
animateRow(startOffsetX, toValue, velocityX / friction); | ||
animateRow(toValue, velocityX / friction); | ||
}; | ||
|
||
const close = () => { | ||
'worklet'; | ||
animateRow(calculateCurrentOffset(), 0); | ||
animateRow(0); | ||
}; | ||
|
||
const tapGesture = Gesture.Tap().onStart(() => { | ||
|
@@ -576,12 +599,12 @@ const Swipeable = forwardRef<SwipeableMethods, SwipeableProps>( | |
|
||
const direction = | ||
rowState.value === -1 | ||
? 'right' | ||
? SwipeDirection.RIGHT | ||
: rowState.value === 1 | ||
? 'left' | ||
? SwipeDirection.LEFT | ||
: event.translationX > 0 | ||
? 'left' | ||
: 'right'; | ||
? SwipeDirection.RIGHT | ||
: SwipeDirection.LEFT; | ||
|
||
if (rowState.value === 0 && onSwipeableOpenStartDrag) { | ||
runOnJS(onSwipeableOpenStartDrag)(direction); | ||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'd move this declaration at the top of the file (somewhere around
SwipeableExcludes
type). I know it is JS and it works, but I think it's a good practice. Also if you read code from the top you know whatSwipeDirection
is (at first I thought it was anumber
, then I saw the declaration)There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
done in a329a6d