Search code examples
javascriptanimationmobilemeteoriron-router

How would I animate a page as I change routes?


Im working on a meteor based app for mobile, and as a result, Im trying to make certain elements animate away when you leave a template. Im currently stumped though insofar as how to delay a route from running, until after an animation is run. I'm okay with setting the delay specifically, and just fitting all out animations into that timer.

So what I've tried to do so far, is use Iron Routers "onStop" function to trigger a velocity animation, Ive also tried used the Template.foo.destroyed call back, but while I can write animations, I cant get them to show up before the next template is loaded. Im willing to try something else entirely if it's a more reliable way to trigger animations based on route changes.


Solution

  • Meteor will be getting better support for animations in the future.

    For now, you can use Meteor's undocumented _uihooks feature. For example, let's say you have a layout template called layout, and all of your routes render a template with a top-level element directly inside a parent container from layout:

    <template name="layout">
      {{> banner}}
      {{> main}}
    </template>
    
    <template name="main">
      <main>
        {{> yield}}
      </main>
    </template>
    
    <template name="home">
      <article>
        <!-- Home page content wrapped in a container -->
      </article>
    </template>
    
    <template name="posts">
      <article>
        <!-- Route content wrapped in a container -->
      </article>
    </template>
    
    <template name="loading">
      <div>
        <i class="fa fa-spinner fa-spin"></i>
      </div>
    </template>
    
    Template.main.rendered = function() {
      var $window = $(window);
      var main = this.firstNode;
    
      main._uihooks = {
        insertElement: function(node, next) {
          var newPage = $(node);
    
          newPage.css("opacity", 0);
          main.insertBefore(node, next);
          newPage.animate({opacity: 1}, 300);
        },
        removeElement: function(node) {
          var oldPage = $(node);
          var offset = oldPage.offset();
    
          // Position the route container to be removed so that it doesn't get
          // pushed down by the new route's template.
          // This may be different for you depending on your layout.
          oldPage.css({
            width: oldPage.innerWidth(),
            position: 'absolute',
            top: offset.top - $window.scrollTop(),
            left: offset.left
          }).animate({opacity: 0}, 300, function() {
            oldPage.remove();
          });
    
          $window.scrollTop(0);
        }
      };
    };
    

    Note that depending on your layout and the complexity of the animation you want, you could probably make this work using classes and CSS transitions rather than imperatively setting CSS properties and animation.