Repository navigation
Description
Version
v5
Reanimated Version
v3 (the form has no v4 option: seen on Reanimated 4.7.1, and on 4.1.1 in the snack)
Gesture Handler Version
v2 (the form has no v3 option: seen on Gesture Handler 3.3.0, and on 2.28 in the snack)
Platforms
iOS, Web
What happened?
On Reanimated 4 the library's default spring takes about 680 ms to finish, although the sheet is within a pixel of its target after about 430 to 500 ms. Until the spring finishes, onChange / onClose / onDismiss have not fired, a modal has not unmounted, and a BottomSheetBackdrop still takes touches (its touchability follows animatedIndex reaching disappearsOnIndex). So a tap on the screen right after a sheet has visibly closed is swallowed.
Cause. The default spring in src/constants.ts (the default on iOS, and on web since v5.2.0) is told when to stop with restDisplacementThreshold: 10 and restSpeedThreshold: 10. Reanimated 4 removed both options (migration guide): withSpring now stops by a relative energyThreshold (default 6e-9) and silently ignores the two old keys. The same two keys are in src/utilities/getKeyboardAnimationConfigs.ts (the keyboard easing). Both are unchanged in v5.2.14 and on master.
Measured in the snack below (web, Reanimated 4.1.1), time from snapToIndex(0) / close() to onChange:
animationConfigs |
onChange fires after |
|---|---|
the library's spring, as in src/constants.ts |
683 to 687 ms |
the same spring with energyThreshold: 1e-5 |
459 to 465 ms |
Calculated by stepping Reanimated's own spring functions with the library's spring at 60 fps, for a travel of 300 to 800 px:
| within 1 px of the target | animation ends | |
|---|---|---|
| Reanimated 3.19.1 (ends below 10 px and 10 px/s) | 433 to 500 ms | 467 to 533 ms |
Reanimated 4.7.1 (default energyThreshold) |
433 to 500 ms | 683 ms |
Reanimated 4.7.1 with energyThreshold: 1e-5 |
433 to 467 ms | 467 ms |
Android is not affected: its default is the 250 ms timing.
Workaround. Pass the same spring with an explicit threshold:
<BottomSheet
animationConfigs={{
damping: 500,
stiffness: 1000,
mass: 3,
overshootClamping: true,
energyThreshold: 1e-5,
}}
/>This does not reach keyboard-driven moves, which take their config from getKeyboardAnimationConfigs.
Suggested fix. In both files, replace the two removed keys with energyThreshold when running on Reanimated 4.
Reproduction steps
- Open the snack on web (or iOS).
- Press "Next animationConfigs" once, so it reads "library spring". Press "Open", then "Close":
onChangefires about 680 ms after each call. - Press "Next animationConfigs" again ("spring + energyThreshold") and repeat: about 460 ms, with the same visible motion.
- On iOS the first setting, "library default", shows the same 680 ms. On web it only does from v5.2.0, where the spring became the web default; the snack is on v5.1.7 (see the note below).
Reproduction sample
snack.expo.dev: gorhom spring ends late on Reanimated 4
Note on the snack's version: it pins v5.1.7 because Snack cannot bundle v5.1.8 or later. Its bundler answers Snack does not support non-semver version references, found: "react-native-reanimated" = ">=3.16.0 || >=4.0.0-", so no release that declares Reanimated 4 support loads in Snack at the moment. The spring config is the same in v5.1.7 and v5.2.14.
Relevant log output
No response
Activity