Smooth fade in / fade out transitions for blaze and reactiveVars

Hey All,

I’m hoping someone can shed some light on an area I gave up on trying to solve a long time ago since there was no good solutions at the time.

I have dom elements that reactively show based on a reactive var being set to different values. When they change its just that jarring one out and on in of dom elements changing.

I’m looking for something that is not just toggling classes with jquery but a real programmatic solution and not using some hack like setTimeout. I guess something that watches tracker maybe? Does this exist?

Oh fun! I did this with swipe and fade. There isn’t really a way of doing it without a setInterval as reactive vars only do something when their data changes. But you only need one. I had a reactive vars that tracks transition state with a value between 0-100 that is triggered on swipe or when pressing a next/prev button. The interval will increment the transition state by some amount every x ms until it hits 100. This allows you to make the transition last as long as you want. The rest is done with css styling that reactively updates. Specifically position and opacity. For fade the pages are stacked with display: none and when you transition from one page to another both pages have display: block and an absolute position. One page has opacity: 1 and the other 0. As the transition progress changes they slowly switch, giving you the fade effect. When the transition completes the previous page is set to display: none again. For swipe the left position is updated for each

With Blaze I have found that CSS animation via classes is the best solution. Fortunately there are some events that fire and you can hook into those to change internal state.

That doesn’t sound very fun at all. Thanks for the idea. I have done something similar in the past but didn’t like that either. Setting the timeout handle in a reactivevar so you can kill it if they click again and stuff like that. Really wish there was an afterflush autorun type thing for firing after dom changes.

This looks intriguing:
https://atmospherejs.com/mcissel/flow-transition

There is a way to do it, there’s an undocumented API in Blaze for this called _uihooks

Here’s the changelog entry from Meteor 0.8.2 when it was added in June 2014

  • Add preliminary API for registering hooks to run when Blaze intends to
    insert, move, or remove DOM elements. For example, you can use these
    hooks to animate nodes as they are inserted, moved, or removed. To use
    them, you can set the _uihooks property on a container DOM
    element. _uihooks is an object that can have any subset of the
    following three properties:

    • insertElement: function (node, next): called when Blaze intends
      to insert the DOM element node before the element next
    • moveElement: function (node, next): called when Blaze intends to
      move the DOM element node before the element next
    • removeElement: function (node): called when Blaze intends to
      remove the DOM element node

    Note that when you set one of these functions on a container
    element, Blaze will not do the actual operation; it’s your
    responsibility to actually insert, move, or remove the node (by
    calling $(node).remove(), for example).

There’s a couple of packages that use this:

I’ve gone with a different approach, using a HOC (Higher Order Component) in Blaze and css transitions.
It starts by finding all child elements with the animate class, attaches css transition event listeners to work out when the transition has completed, and removes the animate class to start the transition

Code is here:

And can be used like so:

<template name="todoList">
    <ul class="todo-list">
      {{#animate}}
      {{#each todo in todos}}
      <li class="animate">{{todo.body}}</li>
      {{/each}}
      {{/animate}}
    </ul>
</template>
.todo-list li {
  opacity: 1;
  transition: opacity 1s;
}
.todo-list li.animate {
    opacity: 0
}

To have new todos fade in or out for 1 second when added or deleted

Unfortunately _uihooks have the weird caveat of needing a parent element to attach the handlers to, so if the parent is dynamic, the element might be replaced and lose the hooks.

I’ve also tried with css animations, using the same event based approach, but I had dificulties covering the case where an animation has a start-delay. I’m sure it can be fixed, but css transitions were good enough for my case.

In the past we’ve also used Blaze.renderWithData to manually control rendering and animated elements with gsap, which is very very verbose and your templates stop being declarative

5 Likes

This is a really cool interface, on the “insert” side, isn’t it equivalent to the following onRendered callback?

Template.todoList.onRendered(function() {
   this.$('li').removeClass('animate');
})

The remove portion is really cool! I could see it being really cool combined with blaze-layout or similar, to transition full pages!

Not quite, because this will trigger for all renders. So if you’re reactively adding items, they get animated as well without needing to wrap them in their own template with their own onRendered

1 Like