CSS is Awesome Happy-CSS

Dealing with the daily frontend trouble with Martin Muzatko

Interaction Design with Riot - Smooth Vanish

9. Lesson of RiotJS series

Dropping in some animation isn't always that easy - especially if javascript is involved. I'll show you some possibilities.

Interaction Design is considered a well-paid job, since all the things we consider "fancy" helps the user to better understand what happens with the application in front of him. I recently read a great article about UX and how interaction design finds its place in usability and accessibility.

#Animate.css

When creating Riot Ravel - a new webapp to find, create and share Riot components online - I wanted to make it look and feel more natural than just snapping into place whenever items are added or removed.

This is how it looked without animations or transitions.

Now I added animate.css and transitions to inputs and the search results. All I had to do, is adding the classes animated and fadeIn or zoomIn, etc.

The result looks a lot better, but still there are a few flaws.

These sound really complex to solve. Without adding a lot of code (especially javascript) we aren't going to solve anything beyond blending things in.

#Gracefully remove items

There are a few online demos out there with their own take on how to slowly remove items. The most common solution is to add a class when removing that item and then listening to onanimationend.

var removeReminder = function(id)
{
    var item = $('#' + id )
    item.addClass('removed-item')
        .one('webkitAnimationEnd oanimationend msAnimationEnd animationend', function(e) {
                $(this).remove()
         })
    deleteReminder(id)
}

While this works great for items you manually remove, since you controll the trigger. This approach does not work for our filtered list. We would have to manually listen to filtered items for every section. This creates more code than we are happy to add.

#What are the Alternatives?

I always tripped over the MutationObserver, when I wanted to accomplish something like after-removal-animation. I also found a great article on how to (ab)use that feature, to undo/redo changes in the DOM. So the idea is that after removing an element (doesn't necessarily need to be the result of user input) we put the element back where it belonged and add classes to animate the element. Then we remove it again after the animation has ended.

Unfortunately, the Browser support only starts from IE 11.

I created a mixin to observe changes and then slowly fade out the items. I wanted this to be as easy as possible. To belong to the libraries that are built in a "Just-Add-Water" fashion, to spare you the amount of configarution required.

It works great for Objects, but not so for Arrays. It looks like the DOM lifecycle works different when removing items from an Array. I'm going to further investigate the problem and expand that mixin to make it work for arrays. As soon as I find something new, I'll update this article to also work for the Array lifecycle.

You can grab the mixin on Gist.

#How to set it up

Important is, that you only start to listen after mounting the tag this.on('mount', function(){...}). Otherwise you'll also listen to the first changes of the template, e.g. removing the curly bracket-tags and swapping them with the rendered tags.

This is interesting, because this way I were able to have a closer insight when and how Riot mounts the tags, but back to topic:

<my-tag>
    <div name="items" each={set in data}>{set}</div>
    <script>
        this.mixin(interact)

        this.data = {your:'data', is:'an', object : ['or', 'an', 'array']}

        this.on(
            'mount',
            function()
            {
                // Important! You don't insert the data in the function,
                // but the element/DOM node it affects (hence name="items" and this.items)
                this.dataFade = new this.interaction(this.items)
            }
        )
    </script>
</my-tag>

you create a new interaction object. You can save the object if you want to, if you have to stop listening to DOMchanges later on. The MutationObserver starts on it's own when you construct the Object. One line of code - thats it!

For more indepth configuration and further hacking, see the Readme.

So we solved our first problem - smoothly removing items.

Next up: A smooth reflow.

#Known Issues

The MutationObserver only works for standard HTML, it looks like it has problems with observing mounting and unmounting custom tags. I'll further investage and let you know ASAP!

#Summary

Some motivation for you: I had no clue about MutationObserver when I started this topic. After searching for ways to achieve that little extra of end-Animation to no avail, I started building my own solution. You can do that too! You can combine the bits of online information together and stitch together a new plugin that solves a painful problem.

#Using the Mixin outside Riot

You can use that mixin outside of Riot. Instead of new this.interaction you do new interact.interaction. For the first argument, you can use jQuery to fetch a parentnode or use document.querySelector('#id').

Please let me know in the comments if you use my mixin or created something to solve so easy but yet hard to achieve.