react-spring: Inertia/momentum incorrect behavior, and unexpected value jumping

🐛 Bug Report

This is a bug for tracking the overall not-quite-right behavior of 1:1 animation with momentum at the end. I started thinking about this after my issue on react-use-gesture last weekend. Here are some earlier reports and discussions from react-spring and react-use-gesture:

https://github.com/react-spring/react-spring/issues/887 https://github.com/react-spring/react-spring/issues/1010 https://github.com/react-spring/react-use-gesture/issues/132

This is a problem that comes up most commonly with gestures, but I believe the solution lies in the animation primitives. Mapping gestures 1:1 with the animation requires using the immediate parameter, but doing so ends up breaking the expected velocity/momentum for the end of the animation/gesture. You can get kind of close to the desired behavior by using a stiff spring config without the immediate parameter, but this isn’t ideal. HCI research has shown that the desired behavior for these types of gestures is a 1:1 correspondence.

The previous suggested solutions for building this behavior have been custom hooks outside the library, but hopefully we can all agree that this is something that should be covered by the library. Previous solutions added a concept of decay, but I think the library already has all of the concepts/APIs we need, it’s just a matter of fixing how velocity is calculated when using immediate.

Expected behavior with reproduction

This is a demo of some horizontal scroll areas nested in a vertical scrolling area. Both of them have “touch” scrolling enabled; the scrollLeft and scrollTop are being animated, in order to co-exist with native browser scrolling.

https://codesandbox.io/s/react-spring-inertial-scroll-challenge-qh4wr

The desired behavior is exemplified by the native scrolling behavior on macOS with a trackpad – 1:1 mapping of scroll gesture to position, with momentum proportional to the spring config when ending the gesture with velocity. The animated value should not overshoot its target.

The touch scrolling demo implementation does have some momentum, but only a tiny bit; not at all like you’d expect. Here’s a gif showing how much momentum I could get when trying my hardest to “sling” it

missing inertia

Environment

  • react-spring v9.0.0-rc.3
  • react v16.8.12

About this issue

  • Original URL
  • State: closed
  • Created 4 years ago
  • Reactions: 5
  • Comments: 16 (6 by maintainers)

Most upvoted comments

That works brilliantly, thanks @joshuaellis!!!

I played around with your tweaks a little more, and it looks like the vxvy Vector2 parameter to onDrag is already scaled with the direction, so that can be used instead of having to scale the velocity Vector2. I updated my 9.2.0 beta sandbox with that change. I also converted it to TypeScript and added a Leva range control for the decay config to play around with.