Smooth Animation on Mobile Web

Smooth Animation on Mobile Web

Presented at Velocity Conference 2014: http://velocityconf.com/velocity2014/public/schedule/detail/34995.

With powerful smartphone platforms and high-performance web browsers, there is no more excuse for laggy UI animations on a mobile web application. The “best practice” list of client-side mobile optimization is pretty well-known: use optimized JavaScript, requestAnimationFrame, event throttling, GPU acceleration, and so on. In this talk, those best practices will be put to the test in a series of practical user interface patterns, from a silky smooth kinetic scrolling example to the use of CSS 3-D to clone the infamous Cover Flow effect.

0284b8950e0f4a57bcc092d4dbb98d97?s=128

Ariya Hidayat

June 26, 2014
Tweet

Transcript

  1. 4.
  2. 5.
  3. 11.

    No Native Browser Scrolling view = document.getElementById('view'); if (typeof window.ontouchstart

    !== 'undefined') { view.addEventListener('touchstart', tap); view.addEventListener('touchmove', drag); view.addEventListener('touchend', release); } view.addEventListener('mousedown', tap); view.addEventListener('mousemove', drag); view.addEventListener('mouseup', release); Intercept mouse events Intercept touch events
  4. 13.

    Tap vs Release function tap(e) { pressed = true; reference

    = ypos(e); e.preventDefault(); e.stopPropagation(); return false; } function release(e) { pressed = false; e.preventDefault(); e.stopPropagation(); return false; }
  5. 14.

    Drag: Delta + CSS Transform if (pressed) { y =

    ypos(e); delta = reference - y; reference = y; scroll(offset + delta); } function scroll(y) { offset = (y > max) ? max : (y < min) ? min : y; view.style[xform] = 'translateY(' + (-offset) + 'px)'; }
  6. 15.

    Scroll Threshold delta = reference - y; if (delta >

    2 || delta < -2) { reference = y; scroll(offset + delta); }
  7. 16.

    User taps the screen pressed = true (touch) reference =

    20 (view) offset = 100 User drags 7 px up (touch) y = 13 (touch) delta = 7 (view) offset = 107 translate the document by 7 px User releases finger pressed = false
  8. 21.

    Steady touchstart Pressed Manual Scrolling Automatic Scrolling touchend timer tick

    touchmove touchend and zero speed touchend and positive speed timer tick decelerate to zero speed touchstart
  9. 23.

    Manual Scrolling Automatic Scrolling Track touch position Scroll the view

    Compute the “launch” velocity Scroll the view Reduce the velocity
  10. 24.

    Continuous Velocity Tracking function tap(e) { pressed = true; reference

    = ypos(e); velocity = amplitude = 0; frame = offset; timestamp = Date.now(); clearInterval(ticker); ticker = setInterval(track, 100); e.preventDefault(); e.stopPropagation(); return false; } Call track() 10 times/second Rate throttling
  11. 25.

    Velocity = Change of Position function track() { var now,

    elapsed, delta, v; now = Date.now(); elapsed = now - timestamp; timestamp = now; delta = offset - frame; frame = offset; v = 1000 * delta / (1 + elapsed); velocity = 0.8 * v + 0.2 * velocity; } instantaneous velocity ~ 100 ms optional filter
  12. 29.
  13. 32.

    Scroll offset at a given time Final scroll offset Amplitude

    (how far the final scroll offset is) Time constant
  14. 33.

    amplitude = 0.7 * velocity; target = offset + amplitude;

    requestAnimationFrame(autoScroll); Setup for Automatic Scrolling
  15. 34.

    function autoScroll() { var elapsed = Date.now() - timestamp; scroll(target

    - amplitude * Math.exp(-elapsed / timeConstant)); requestAnimationFrame(autoScroll); } Continuous Automatic Scrolling
  16. 36.

    The flick (=launch velocity) determines how far the list will

    stop It does not impact when the list will stop scrolling 1 2
  17. 40.
  18. 45.

    As Simple As... function autoScroll() { var elapsed = Date.now()

    - timestamp; scroll(target - amplitude * Math.exp(-elapsed / timeConstant)); target = Math.round(target / snap) * snap; requestAnimationFrame(autoScroll); }
  19. 46.

    As Simple As... function autoScroll() { var elapsed = Date.now()

    - timestamp; scroll(target - amplitude * Math.exp(-elapsed / timeConstant)); target = Math.round(target / snap) * snap; requestAnimationFrame(autoScroll); } Integer multiples of snap
  20. 49.
  21. 50.
  22. 53.

    function scroll(offset) { var slow = Math.round(offset / 2); var

    fast = Math.round(offset); ... } snap = screen width
  23. 58.