Internals: Flick

davidaurelio edited this page Sep 17, 2010 · 44 revisions

This page describes how flicks are calculated and applied within TouchScroll.

Calculation

Input data for calculating flicks is:

  • The last two touch events – the time delta is used to calculate the initial speed, and the vector between event coordinates defines the direction of the flick.
  • An average friction factorconfig.flicking.friction – defining the deceleration factor per millisecond that is used to pre-calculate flick duration and distance.
  • Minimum speed for the pre-calculation (config.flicking.minSpeed). This value is the virtual speed where scrolling would stop – it would continue endlessly when only multiplied with the friction factor.

Variables used are minimum speed (v_{min}), initial speed (v_{init}), friction (f), duration (t), and distance (d)

[ t = \frac{\log \frac{v_{min}}{v_{init}}}{\log f} ]

[ d = v_{init} \cdot \sum_{i=o}^{t} f^i = v_{init} \cdot \frac{1-f^{t+1}}{1 - f} ]

Application of Flick and Bounce

The pre-calculated distance and duration are then applied as transform and transition-duration. The transition-timing-function is configured in config.flick.timingFunc.

If the flick goes beyond the scroller bounds, the individual portions of duration and distance are calculated using an instance of CubicBezier project page for each axis. The timing function bezier is divided into two parts. That means, that the flick and bounce animations are completely separated. This is needed to create the rubber-band effect. Bounce duration and distance are then multiplied by config.elasticity.factorFlick. These two animations/transitions need to be chained in order to appear as a single one.

Chaining Flick and Bounce Animations

During the development of TouchScroll I have tried several methods of chaining the two animations, each having individual problems.

  • Using two <div> elements: The flick animation is applied to one of the elements and starts immediately. The bounce is applied as transition on the second element, using the flick duration as transition-delay. Drawback of this method is the usage of up to four nested transforms, which has negative impact on rendering performance, especially on weak devices (3G and below).

  • Using @-webkit-keyframes: I tried to create a set of @-webkit-keyframe rules for each instance (at instantiation time) and update them for each flick. Changing the individual keyframes seems to trigger an expensive calculation on iPhone 3G, leading to a visible delay before applying the animation. This would be the preferred method. If anybody feels challenged to make this work, try commit 1f95f15 as a starting point.

  • Chaining on webkitTransitionEnd events: The most natural approach. Works beautifully, but not on iPhone 3G: Because webkitTransitionEnd events fire late, there is a visible gap between flick and bounce animation.

  • Chaining with timeouts (current method): Solves the delay problem that the webkitTransitionEnd-method has. Problems occur, when heavy rendering is going on (can happen when scrolling fast), ore the JavaScript thread is busy, which can lead to ugly delays.

Currently the fourth method is used, and all methods have been tried. The quality of the animation still needs to be improved, and any help is appreciated.