Pause routing in Meteor's Iron Router while a page transition completes
Asked Answered
P

2

5

In my Meteor app I have some complex page animations that require a few seconds to complete (instructive animations take priority over page transition speed).

There's an out state and an in state in the animation. For simplicity's sake, let's say I need to fade out one page, and then fade in the next, but that I want those fades to take multiple seconds. To do this I use Meteor's Iron Router to call some animation functions that manipulate the CSS.

lib/router.js

animateContentOut = function(pause) {
    return $('#content').removeClass("animated fadeIn");
}
Router.onAfterAction(animateContentOut);

animateContentIn = function() {
    return $('#content').addClass("animated fadeIn");
}
Router.onAfterAction(animateContentIn);

This is based on a great tip from Manuel Schoebel and the fadeIn works. However, my fade animation takes several seconds. So the user only sees the first few miliseconds of the fadeOut animation because it starts and then the router quickly navigates away to the new route before the animation completes.

So my question, is: how can I tell the router to wait for the animation to complete or for a setTimeout on an action in the onAfterAction call? Is there a better hook to use when animating the process of leaving a page?

Puggree answered 23/7, 2014 at 15:57 Comment(2)
You may want to see github.com/tmeasday/iron-transitioner and its current branches in github.com/percolatestudio/iron-transitioner.Neurasthenic
I dug into the different branches on the transitioner, but I couldn't figure out how to make it work with my view/template structure. It seemed like I couldn't do transitions between different views. But, I might not have looked close enough.Puggree
T
6

It depends what's generating the change of page. If it's a link/button/generic event, then rather than using an anchor href, you could just register an event like below, and store the route you want to move to (like "/home") in the data-route attribute of the anchor tag:

Template.links.events({
    'click a': function(event) {
        var _currentTarget = event.currentTarget;
        $('#content').removeClass('animated fadeIn').on('transitionend webkitTransitionEnd oTransitionEnd otransitionend MSTransitionEnd', function() {
            Router.current().redirect(_currentTarget.attributes['data-route'].value);
        });
    }
});

That should begin the fade out, and actually wait for it to end before changing the route. A few caveats:

  1. It won't do anything for you if the user manually changes the url, and you'll have to add it to every piece of logic that changes page - to avoid either of these problems you'll probably have to pick apart the iron-router code and find a way to hook into page changes, which I'm not in a position to do at present!
  2. I haven't tested cross-browser, but it works fine in Chrome.
  3. In my experience you need to be careful with the currentTarget property of an event in Meteor - I don't think it will always give you what you expect, but this is something I need to take up separately.
Tamis answered 23/7, 2014 at 19:40 Comment(4)
Thanks, this set me on the right path. I still wish there was a way to do this at the router level, but I'm really only interested in reacting on the user events so this get's me there. I ended up putting a Router.Go('routeName' _id) call inside of a setTimeout function for a bit more timing control.Puggree
"It depends what's generating the change of page". I agree, but does anybody know the correct pattern if the change of page is generated by a link href="{{pathFor 'routeName'}}" ?Switch
physiocoder, I wasn't ever able to get this to work with the the href pattern. I moved most of my page navigation over to click events so that I could enact these page transitions. It does have the benefit of putting all your navigation logic in one place, but it still feels like a bit of a hack.Puggree
thanks @bryankennedy, I managed to enable page transitions even with href pattern. The caveat is that .render() is async so it is not guaranteed to have DOM elements to manipulate in onAfterAction. So the trick was just to put setTimeout(function() {$('element').addClass('animation');}, 0); in onAfterAction hook. Hope this can help. Of course couldn't animate fade out/disappearing of old route, but only animation effect of new route.Switch
D
3

I have run into same question, when trying to combine Meteor with Framework7. Here is solution that absolutely works for me:

Meteor.startup(function () {
    var _animationEnd = 'oanimationend animationend webkitAnimationEnd otransitionend oTransitionEnd msTransitionEnd mozAnimationEnd MSAnimationEnd',
            _enterAnimation = 'fadeIn animated',
            _leaveAnimation = 'fadeOut animated',
            _animate = function ($el, anim, next) {
                return $el.addClass(anim)
                        .on(_animationEnd, function () {
                            $(this).removeClass(anim);
                            next && next();
                        });
            };
    Router.onAfterAction(function () {
        _animate($(".view-main"), _enterAnimation);
    });
    $(document.body).click(function (event) {
        var $t = $(event.target).parents().andSelf().filter("[href]:last"), url;
        if ($t.size() && (url = $t.attr('href'))) {
            var currentRoute = Router.current();
            _animate($(".view-main"), _leaveAnimation, function () {
                currentRoute.redirect(url);
            });
            event.preventDefault(), event.stopPropagation();
        }
    });
});

Comments and pros vs. solution provided by richsilv:

  • It works for all templates you ever use, since it's using body events
  • It works with any element, even a.href's, since preventing click event from default and further propagation
  • It is more compatible with other browsers, since more "animationend/transitionend/etc" events listed
Disk answered 28/9, 2014 at 10:32 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.