-
-
Notifications
You must be signed in to change notification settings - Fork 197
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
UI - ScrollView: Momentum scrolling + Bounce #288
Comments
Popmotion has one library called "Pure" that has a functional design and has made a lot of good decisions to shape the API so that is usable in different environments (desktop, iPad, VR...), like separating It recently added
Maybe this could serve as inspiration, it seems many of the data types and functions could be ported to Reason idioms pretty nicely. Idk if these interpolation primitives should leave in Revery in the long-term? 🤔 but we could definitely start experimenting "in-repo" 😄 @bryphe Do you think taking the interesting parts from Popmotion to allow for inertia, and putting them under a single module in Revery would be a step in the good direction? Edit: I stumbled upon these two articles that include great explanations on how iOS scrolling works: There is an implementation here. |
Hey @bryphe! I was investigating this and realized a couple of things:
|
Neat! I didn't know about this library. I do like the functional API they provide (cc @wokalski - we were talking about animation today), and the inertia/springs/momentum would be a natural fit for this work on scrolling.
Experimenting in-repo sounds fine; and then we could extract them out to a separate library as it makes sense 👍 Starting with a module and porting over these primitives sounds like a great way to experiment. Would be fun to have an example app that shows a similar set of animation/physics primitives! The only concern I had is I couldn't find license info for Popmotion Pure - do you happen to know where we can find that? (If it's not compatible with MIT, we might not be able to do a direct port). Nice find on those links, too! Some interesting observations that I missed on my cursory investigation, like:
That accompanying example code certainly be useful (handles cases I didn't think about above - like snapping to a list item).
Interesting, was curious about this. I think if the platform supports it, we should use the platform implementation. But Windows definitely doesn't add momentum by default. And I don't think the platform would support the 'bounce' at the edges, since it wouldn't know the extents of our 'virtual' scrollview - so we'd have to reimplement that piece, at the very least. An option would be to use a conditional flag that we pass per-platform whether we should 'add' momentum, or just use the values as-is.
Ouch, yes, that's quite a difference! It's possible there is some differences in scaling going on - we may also need some per-platform normalization. Taking a look at a few different references:
I realized our current implementation of the 'scroll' JS stub doesn't take into account I'll double-check and see how the values reported on the JS / native side relate on Windows. I'll try using the same mouse on my Mac Mini also, and see how the values compare for each 'notch'. |
It seems to be MIT (
Unfortunately I don't have any Windows machines at hand, so maybe I could start working on the "bounce" / "rubber band scrolling" first, as that part is clearly needed for both platforms?
I checked |
I just found this golden piece of data:
|
Sounds good to me! Regarding normalization: I just ran the tests on
For our I also stumbled across this post too which had some ideas around normalizing scrollwheel values in the browser: https://stackoverflow.com/questions/5527601/normalizing-mousewheel-speed-across-browsers. That code normalizes to a pixel-level scroll, which I think would be reasonable for us to adopt as well. We could have normalize the scroll values to
Then, in Revery, we could multiply the revery/src/UI_Components/ScrollView.re Line 104 in 5600aee
Nice find! It seems like the If it would be helpful for us, we could track an issue on GLFW or create a fork to add this as part of the context it sends about scroll/wheel events. And we might need to look at Direct Manipulation or the direct touch APIs as we start implementing touch/gesture support. I also am not sure if it would build in momentum into the calculations, although it seems like it does add some inertia for certain transitions (we'd also have to see if it would work in the context of GLFW/OpenGL - it might need to manage the rendering for it to work). We'd also have the option for Windows of just using the direct pointer APIs, without Direct Manipulation, w/o momentum. |
Woah amazing research!! 😮 Seems there's a lot of room to normalize scrolling across platforms.
It seems that in the first answer edited solution they are giving up on normalizing altogether, no? I'm referring to: var handleScroll = function(evt){
if (!evt) evt = event;
var direction = (evt.detail<0 || evt.wheelDelta>0) ? 1 : -1;
// Use the value as you will
};
someEl.addEventListener('DOMMouseScroll',handleScroll,false); // for Firefox
someEl.addEventListener('mousewheel', handleScroll,false); // for everyone else It seems Firefox doesn't even have There's also one answer in that same question that is pointing to this code from Facebook's
The normalization would happen upstream in
Nice! I'm not sure if we'll need this yet. The only reason I can think of is because we want to use our own "inertial stream" of events instead of the native one, but it's probably better to leverage what the native platform provides. |
Thanks, right back at you @jchavarri ! 😄 Definitely, I think we can get reasonably consistent behavior across platforms.
Ah yes... That facebook code seemed to me like the best answer in that post. And that is interesting... they mention Safari's edge case in the comments (and it seems like older versions of Safari actually exhibit similiar behavior as Chrome/IE!) but indeed, it doesn't seem to be accounted for in the code - perhaps that's reason enough for us to leave it out, too.
Exactly what I was thinking... If we skip out on Safari - then it seems like it would just be a matter of switching to One thing I wasn't sure of, though, is that it seems native OSX might need a 'normalization factor' of It might be that this is specific to mouse scrollwheel and trackpad gestures are reported differently, though - so perhaps adding that multiplier might make trackpad gestures too fast. 🤔 |
Momentum scrolling is a natural interaction for scrolls - it'd be great to have this instead of the 'instant' scroll behavior today in #195 . The momentum scrolling gives more context during the interaction and more control - making it easier for you to scroll to the right element.
A physically based scroll model
When using the mousewheel, apply a 'force' to the scroll thumb.
Force = mass * acceleration
, and we can simplify and just assume a mass of 1 for the thumb, which would beForce = acceleration
. Each frame, we'd increase the velocity of the thumb based on theacceleration * t
. In addition, we'd adjust the actual position of the thumb based on velocity (position = position + velocity * t
). This would be similiar for other gestures, like a scroll gesture on touch.When clicking on the track, use an appropriate force. This would be similiar to the mousewheel, but would need to be calibrated to use more 'force'.
Model 'friction' - without any friction, as soon as you scroll, the cursor would just fly to the end! So we'd need to model friction. Friction is a constant resistive force - this could be simplified by subtracting the acceleration by a constant factor * t every frame. This could be modeled in more detail by static friction and kinetic friction, but I'm not sure if that actually is useful for the UX.
Model 'springs' at the boundaries - Without modelling any sort of spring at the boundaries, the scroll would be smooth until the end, and then it would just instantly stop. iOS implements a nice 'bounce' effect - this can be modeled physically by using the spring equation
F=-kx
, wherek
is a spring constant (can play with different values), andx
is the distance passed the boundary. In effect, as you go further out the boundary, the force applied increases - this gives a nice bounce effect. Different values ofk
give different types of bounce (the stiffness of the spring). We'd have to calculate the force due to spring acceleration with this, and apply it to our formula too.Implementation
To manage momentum scrolling, we'd need to store some state for each scrollbar:
We'd also need to track the time between each frame (
t
or delta time, to apply to the equations).While the physics 'simulation' is in effect, we'd want to render every frame at (60fps). This could be implemented by setting a state value (like the last frame time) every time we want a new frame - this will trigger a re-render because the reconciler will mark the tree as dirty. We should do this whenever a force is applied, or whenever the velocity is greater than a certain threshold. Once the scrollbar has 'settled' and is no longer in motion, we should no longer re-render.
Each time we render, we'd want to calculate a new position:
And a new velocity:
And the real interesting stuff is the acceleration:
We could use an
option(Time.t)
to track the last frametime. If it isNone
, there is no animation in progress - if it isSome(t)
, we know there is an animation and we should use it to calculate new state. Whenever a force is applied (via mousewheel, clicking on a track, etc), we should setSome(t)
. Once the velocity settles below a certain threshold, we should set it back toNone
.This would be a fun experiment in applying animations in Revery, but a lot of moving parts!
Other Challenges
We currently use the
Slider
control as the scrollbar - would it make sense to factor it out to a separate control? I'm not sure the momentum aspect makes sense for aSlider
, so it seems logical to factor that out to a separateScrollBar
component.Platform-Specific Concerns
This isn't a platform-native feature - but with this physical model, we could potential tweak the constants (the friction applied, the force applied, the spring stiffness) to work well on different platforms. For example - potentially on Windows, the 'spring' at the boundaries of the scroll could be much stiffer than on OSX/iOS.
The text was updated successfully, but these errors were encountered: