— 1 of 10 —

Embracing Touch

Cross-platform Scrolling

By Mark Dalgleish

Scrolling is the new hotness

  • Infinite scroll, pull to refresh, end-of-page notices.
  • Scrolling animation (e.g. Scrollorama)
  • Parallax (e.g. Stellar.js)

Scrolling effects work best with inertial scrolling.

They should be perfect for touch platforms, but...

Mobile Safari spoils our fun

  • Overflow:auto doesn't work how you'd expect... no inertia on scroll.
  • Scroll events aren't triggered until scroll has completely stopped.
  • We can't even run code using 'setInterval' while the page is scrolling.

Let's try a simulation of what this looks like...

If you scroll down, you'll notice that the parallax and fading effects won't run while the page is scrolling. Only when the page has stopped scrolling completely will any animation occur.

This is a simulation of how JavaScript execution is halted during scroll in Mobile Safari on iOS.

This is a simulation of how JavaScript execution is halted during scroll in Mobile Safari on iOS.

This is a simulation of how JavaScript execution is halted during scroll in Mobile Safari on iOS.

This is a simulation of how JavaScript execution is halted during scroll in Mobile Safari on iOS.

This is a simulation of how JavaScript execution is halted during scroll in Mobile Safari on iOS.

This is a simulation of how JavaScript execution is halted during scroll in Mobile Safari on iOS.

This is a simulation of how JavaScript execution is halted during scroll in Mobile Safari on iOS.

JavaScript to the rescue

Using a touch scrolling library allows us to imitate native scroll using JavaScript and CSS3 transforms.

iScroll by Matteo Spinelli:
cubiq.org/iscroll-4

Scrollability by Joe Hewitt:
joehewitt.github.com/scrollability/

Zynga Scroller by Zynga:
zynga.github.com/scroller

  • Pretty row 1
  • Pretty row 2
  • Pretty row 3
  • Pretty row 4
  • Pretty row 5
  • Pretty row 6
  • Pretty row 7
  • Pretty row 8
  • Pretty row 9
  • Pretty row 10
  • Pretty row 11
  • Pretty row 12
  • Pretty row 13
  • Pretty row 14
  • Pretty row 15
  • Pretty row 16
  • Pretty row 17
  • Pretty row 18
  • Pretty row 19
  • Pretty row 20
  • Pretty row 21
  • Pretty row 22
  • Pretty row 23
  • Pretty row 24
  • Pretty row 25
  • Pretty row 26
  • Pretty row 27
  • Pretty row 28
  • Pretty row 29
  • Pretty row 30
  • Pretty row 31
  • Pretty row 32
  • Pretty row 33
  • Pretty row 34
  • Pretty row 35
  • Pretty row 36
  • Pretty row 37
  • Pretty row 38
  • Pretty row 39
  • Pretty row 40

Preparing the markup for iScroll

On touch platforms, "wrapper" has its overflow hidden, "scroller" is positioned using CSS3 transforms.

The example markup from Cubiq.org:

<div id="wrapper">
    <div id="scroller">
        <ul>
            <li>...</li>
        </ul>
    </div>
</div>

Selectively initialising iScroll

Using Modernizr we can target touch platforms:

if (Modernizr.touch) {
    var myScroller = new iScroll('scroller');
}

Using Modernizr to style iScroll

#wrapper {
   overflow: auto;
}

/* Target the'touch' class
   generated by Modernizr */

.touch #wrapper {
   overflow: hidden;
}

Scrolling — Desktop vs Touch

Normally we have scroll events, scrollTop, scrollLeft.

Using CSS transforms, we don't have scroll events. We need a cross-platform way to calculate scroll.

Instead of scroll events, let's use an animation loop...

Loop, there it is

(function animationLoop(){
    window.requestAnimationFrame(animationLoop);
    
    // Animation logic goes here
})();

'requestAnimationFrame' is currently prefixed in all supporting browsers.

Opera engineer Erik Moller has written a requestAnimationFrame polyfill.

Normalise the scroll position

function getScroll(elem, iscroll) {
  var x, y;

  if (Modernizr.touch && iscroll) {
    x = iscroll.x * -1;
    y = iscroll.y * -1;
  } else {
    x = elem.scrollTop;
    y = elem.scrollLeft;
  }

  return {x: x, y: y};
}

Get in the loop

var myScroller = Modernizr.touch ? new iScroll('scroller')
                                 : null;
(function animationLoop(){
    window.requestAnimationFrame(animationLoop);
    
    // Now we'll use our 'getScroll' function
    var scroll = getScroll(window, myScroller);

    // Values are now normalised cross platform:
    scroll.x;
    scroll.y;
})();

If you scroll down, you'll now see how Mobile Safari performs during "scroll" when using JavaScript and CSS3 transforms to recreate a native scroll effect. You'll notice that animation can now run smoothly.

This is a simulation of animation while scrolling in Mobile Safari on iOS using iScroll.

This is a simulation of animation while scrolling in Mobile Safari on iOS using iScroll.

This is a simulation of animation while scrolling in Mobile Safari on iOS using iScroll.

This is a simulation of animation while scrolling in Mobile Safari on iOS using iScroll.

This is a simulation of animation while scrolling in Mobile Safari on iOS using iScroll.

This is a simulation of animation while scrolling in Mobile Safari on iOS using iScroll.

This is a simulation of animation while scrolling in Mobile Safari on iOS using iScroll.

That's what I Know

Slides: bit.ly/xscroll

@markdalgleish