Algorithm to implement kinetic scrolling
What are some good algorithms for creating a kinetic scrolling implementation? The feature would be 开发者_如何学Pythontested on a custom UI list. While I am targeting mobile devices (those that do not have this feature built-in), any algorithm or code example from different programming field may also suit.
Since this was originally asked, I have since carefully read the source code for pastrykit, the framework in which apple has exactly duplicated their kinetic scrolling in javascript. There is no particle physics loop- The velocity of the touch is measured at the exact moment that the finger is lifted. From that point forward, the scroll is animated using a simple easing function that uses the velocity as an input parameter.
I implemented one myself recently. These are the steps I took.
- You need to measure the velocity of your cursor (either mouse cursor or finger)
- Implement a simple particle physics loop. Information about how to do that can be found here
- give your particle "bounds" using math derived from the width of your scrolling plane, and the width of your viewport
- continuously Add the the difference between the mouse velocity and the particle velocity, to the particle's velocity, so the particle's velocity "matches" the mouse's velocity for as long as it's moving.
- Stop doing step 4 as soon as the user lifts their finger. The physics loop takes care of inertia.
- Add your personal flourishes such as "bumper" margins, and smooth scrolling "anchor" points that operate on zeno's paradox for calculating motion.
- I nearly forgot: Take the coordinates derived from above, and use it as the location of your scrolling plane.
I will probably open source this code soon. How soon do you need this?
edit:changed the link. Sorry, pointed to slightly the wrong page. edit2: or not? Anyway, original page I linked to was first link on currently linked page.
I just added a scroller widget to my mobile GUI framework, so I thought I'd share my solution here.
Since this is a pretty complex issue, I decided to divide it up into smaller somewhat independent subtasks, as follows:
Basic scrolling functionality: this task consisted in handling drag events in the simplest way, which is by scrolling the contents according to the drag distance and direction. This was relatively straightforward to implement, the only tricky aspect was to know when to start a drag operation vs. when to pass down a tap event to a child widget inside the scrollable area.
Scroll inertia: this one was the most challenging. The idea here is that scrolling should continue for some time after the user lifts the finger, slowing down until it stops completely. For this I needed to have an idea of the scroll velocity. Unfortunately it is not accurate to compute the velocity from a single sample, so while the user is scrolling I record the last N motion events in a circular buffer, along with the time at which each event occurred. I found N=4 to work just fine on the iPhone and on the HP TouchPad. When the finger is lifted I can compute an approximate start velocity for the inertial scrolling from the recorded motion. I defined a negative acceleration coefficient and used standard motion formulas (see here) to let the scrolling die down nicely. If the scroll position reaches a border while still in motion I just reset the velocity to 0 to prevent it from going out of range (the abrupt stop is addressed next).
Flexible scrolling limits: instead of going into an abrupt stop when the scroll reaches the end I wanted the widget to scroll some, but offering resistance. For this I extended the allowed scroll range on both ends by an amount that I defined as a function of the widget dimensions. I've found that adding half the width or height on each end worked nicely. The trick to give the scrolling the feeling that it is offering some resistance was to adjust the displayed scroll positions when they are out of range. I used a scaling down plus a deceleration function for this (there are some good easing functions here).
Spring behavior: since now it is possible to scroll past the valid range, I needed a way to bring the scroller back to a valid position if the user left it out of range. This is achieved by adjusting the scroll offset when the scroller comes to a stop at an out of range position. The adjustment function that I've found to give a nice springy look was to divide the distance from the current position to the desired position by a constant and moving the offset by that amount. The bigger the constant the slower motion.
Scrollbars: the final touch was to add overlay scrollbars, which fade in when scrolling starts and fade out when it ends.
Have you looked at Robert Penner's easing functions?
http://www.robertpenner.com/easing/
IIRC these were originally for Actionscript and have been around for a long time.
I searched a lot. Combined it all together i made my solution.
- While pointer down you should calculate difference in pixels btw each call onTouchMove handler. On touch end save this value in some variable, lets call it diffPx for example.
- Use requestAnimationFrame for continious momentum scrolling. On each frame scroll your container by diffPx =* 0.95 as long as diffPx would not reach some limit value(I used 0.25).
- One important limitation. Do step 2 only if diffPx on pointer up more than limit value(I used 5px).
https://developer.mozilla.org/ru/docs/Web/API/window/requestAnimationFrame
Well i think it would be something like a) get velocity of how fast user scrolled b) when he leaves his finger, auto scroll the list but with a decreasing velocity with an initial velocity of what the user had.
精彩评论