CSS page transitions

This recipe we'll cover how to use gesture events as well as an approach to implementing smooth page transitions using simple HTML, JavaScript and CSS. The end result is an application for flicking through a collection of images fetched from Flickr.

Detecting gesture events

First we'll create a basic Forge app which detects gesture events. We'll include a reset stylesheet to make layout a bit more predictable, and to detect gestures we'll use Zepto.js. All we need to detect gestures is:

$(document).on('swipeRight', function () {
  window.alert("Right");
});

$(document).on('swipeLeft', function () {
  window.alert('Left');
});

The full app so far can be seen on github.

Implementing transitions

Next we want to slide in a new page view when we detect a gesture. There's a few transitions effects that are possible, for this app we'll both move the current content out and pull the new content in at the same time. In order to achieve this effect, we'll simply put both pages in a containing element and apply a CSS transition to that container. Here's what the body of our app looks like now:

<div id="wrapper">
  <div class="page">Flick me!</div>
</div>

Now that's in place, we can use JavaScript to insert a new page horizontally aligned with the current one:

// get reference to current page
var $current = $('.page');

// create page
var $next = $('<div class="page">Page 2</div>');

// horizontally align with viewport
$next.css({
  position: 'absolute',
  top: 0,
  left: (window.innerWidth)+'px',
});

// insert into wrapper for transition
$('#wrapper').append($next);

// Then use Zepto to perform a CSS transition, remembering to clean up when it's finished:
$('#wrapper').anim(
  // we want to shift the container across the width of the viewport
  {translate: (-window.innerWidth)+'px,0'},

  // the animation will take 0.3 seconds
  0.3,

  // the animation will slow down towards the end
  'ease-out',

  // callback to execute when the animation's done
  function () {
    // remove the page that has been transitioned out
    $current.remove();

    // remove the CSS transition
    $('#wrapper').attr('style', '');

    // remove the position absoluteness
    $next.css({
      top: '',
      left: '',
      position: ''
    });
});

You can see this in action here: http://jsfiddle.net/Fqbym/

Making the transition logic more reusable

Now we have the idea of how to do smooth transitions, we can extract the demo into a function that we can use to bring in whatever pages we like. See github for the implementation of our transition function, which allows us to easily specify the wrapper, current page, new page and direction for a transition. Now our overall app logic is simply connecting swipe events calls to transition:

// page generation
function makePage(number) {
  return $('<div class="page">Page '+number+'</div>');
}

// overall app logic
var pageNumber = 0;
$(document).ready(function () {
  $(document).on('swipeRight', function () {
    transition($('#wrapper'), $('.page'), makePage(--pageNumber), directions.right);
  });

  $(document).on('swipeLeft', function () {
    transition($('#wrapper'), $('.page'), makePage(++pageNumber), directions.left);
  });
});

Transitioning in more interesting pages

We can replace our makePage function with anything that creates a DOM element, so let's add something more than just the page number. Flickr has a feed of public photos that requires no authentication so we'll use that for convenience in this tutorial. Since we're loading images, we'll probably want to show an activity indicator until we have an image. It's simple to drop in spin.js for this purpose.

And the code.

That's it

The flexibility of HTML, CSS and JavaScript allowed us to add transitions with minimal markup and without requiring any special APIs - we could easily reuse this technique for mobile websites. Likewise, we can leverage any of the tutorials and techniques out there for mobile web on the Forge platform, as we did for the activity indicator.

Using the Forge platform can take this further - for example we could use forge.file.cacheUrl to cache images in conjunction with forge.prefs so the app works offline.

Feel free to use this transition approach in your projects. This implementation should be simple enough that you can integrate it with most tools you use, as it really just requires a wrapper element for your flickable views to go into. It'd be easy to switch to something like Mustache or underscore.js based templating for the page generation, and Backbone.js for improving separation of concerns. Happy swiping!