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 [1].

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 Setup

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 in Action

There are some nice touches, like the page that is leaving fading out as it slides out.

 

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 [2]. 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. currently in alpha testing at http://beta.getleague.com; check it out if you like but please be kind []
  2. 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. []
Tom Coleman

Co-creator of bindle.me, searching for simplicity, quality and elegance in technology, products and code.

See all Tom Coleman's posts

5 Responses to “Page Transitions in Meteor: getleague.com”

  1. justink says:

    Hey. I realize this is kind of an old post, but would you happen to know the status of this with either 0.5.1 or 0.5.2? I’m getting undefined and null for current and next respectively, and am seeing no errors or warnings in either the client or server side consoles.

  2. Chris says:

    Hi,
    I tried to follow your example at github but it’s not working for me.
    It always shows me the foo template even if I try localhost:3000/bar or some other url.

    My JS:[code]if (Meteor.is_client) {
    MyRouter = ReactiveRouter.extend({
    routes : {
    '' : 'foo',
    'bar': 'bar'
    },

    foo : function() {
    this.goto('foo');
    },

    bar: function() {
    this.goto('bar');
    }
    })

    Router = new MyRouter();
    Meteor.startup(function() {
    Backbone.history.start();
    })
    }[/code]
    And template [code]
    shopTest

    {{{render currentPage}}}

    Template FOOOO

    Template BAAR
    [/code]

  3. Sander says:

    Thanks for sharing these useful bits. Just implemented it in my project.

Leave a Reply

  • Search: