Page Transitions in Meteor

Case study on page-to-page transitions

Update: I’ve released my reactive router and the (complete) transitioner class. Enjoy!

Note: dear readers from the future!: at the time of writing the released meteor version is 0.3.7; as Meteor is a rapidly changing framework, it is likely this post is out of date!

I’d like to briefly walk through the process that we used to implement page->page transitions in the League project.

Meteor apps are single page, client side Javascript applications, but this doesn’t mean you can’t have more than one screen. Smartphone users are more than familiar with the sense of ‘space’ created by having pages slide into one another. In this article I’ll describe how I used a Transitioner class in meteor to achieve page transitions.

HTML

Our main index.html looks like:

<div class="container current_page {{current_page}}">
  {{#if_with current_page}}{{> one_screen}}{{/if_with}}
</div>
<div class="container next_page {{next_page}}">
  {{#if_with next_page}}{{> one_screen}}{{/if_with}}
</div>

The one_screen template as you may guess, draws one screen, much like:

<template name="one_screen">
  {{#if_equals "teams" this}}{{> teams }}{{/if_equals}}
  {{#if_equals "players" this}}{{> players }}{{/if_equals}}
  ...
</template>

The two variables current_page and next_page represent the two pages in the current transition. So if we are transitioning from the teams to the players page, we simply render the teams page into .current_page and players into .next_page and trigger a transition which animates .current_page out to the left and .next_page in from the right:

​First page: Click me!​

​Second page: Click me!​

Here’s an action shot of it happening live:

league-transition-1024x888

Meteor Setup

So where do these variables current_page and next_page come from? The Transitioner. Let’s start this story at the Router. It’s actually a little bit complex, but the short story is that the router sets up a reactive variable representing the current page. Here’s some abbreviated code which uses deps-extensions:

AuthenticatedRouter = Backbone.Router.extend({
  initialize: function() {
    Meteor.deps.add_reactive_variable(this, ‘page’, ‘loading’);
  },
  goto: function(page) {
    this.page.set(page);
  }
});

Route functions call goto() to inform the rest of the application of which page should be visible. The Transitioner basically wraps Router.page() with a delay:

Transitioner = function(Router) {
  Meteor.deps.add_reactive_variable(this, ‘current_page’, Router.page());
  Meteor.deps.add_reactive_variable(this, ‘next_page’);

  Meteor.deps.await(function() { return Router.page() != this.current_page(true); }, function() {
    this.transition_to(Router.page());
  });
};

Transitioner.prototype.transition_to = function(page) {
  var self = this;
  self.next_page.set(page);

  // we defer so that the next page has rendered + is attached before we add the class
  Meteor.defer(function()) {
    $(‘body’).addClass(‘transitioning’)
      .one(‘transitionend’, function() {
        self.transition_end();
      });
  });
};

Transitioner.prototype.transition_end = function() {
  var self = this;
  self.current_page.set(self.next_page(true));
  self.next_page.set(null);

  $(‘body’).removeClass(‘transitioning’);
});

The magic of reactive programming huh? [Simple][1]. I like it.

CSS Setup

This bit isn’t really meteor specific, but you might want to know how to achieve a sliding transition. It’s quite easy if you always want to go left to right:

.current_page, .next_page {
  position: relative;
  @include transition( all 500ms cubic-bezier(0.51, 0, 0.6, 1) 0ms);
}

.current_page { left: 0; }
.next_page { left: 100%; }

.transitioning {
  .current_page { left: -100%; }
  .next_page { left: 0; }
}

Things are more complex when you want different page transitions to have different animations, check out _transition.scss if you want to see how we achieved League’s other animations.

Let me know what you think about my techniques and if you have a better way to do anything!

[1] Actually it’s a bit more complex than this as some of my CSS transitions also require the <body> to have the current and next page set as classes. Also there are edge cases to deal with. But the core idea is there.

Get the Newsletter

5 Comments

  1. Brandon Barney

    This was originally written in 2012…is it still up to date given Iron Router and related changes?

    Apr 3, 2015 Reply
    • Dominic Nguyen

      The techniques were based on an older version of Meteor. For the latest and greatest page-transition patterns, checkout the Local Market example app (made by us) with meteor create --example localmarket. We also created momentum to handle more advanced animations.

      Apr 5, 2015
  2. Emmanuel

    This blog post is missing an image. The main photo connects to the newblog.percolatestudio.com when it should use blog.percolatestudio.com

    Jan 25, 2015 Reply
  3. Son

    Thanks for sharing all high-quality and useful contents about meteor and modern app UX. Appreciate it!

    Dec 14, 2014 Reply

Previous Post

Animations in Meteor

State of the game

Next Post

Augmenting Meteor

Plugging gaps with external services