React and the Web Animation API - KeyframeEffect

In the last post, we saw the easy way to animate a DOM element by using the new element.animate method. This is useful if you want to play simple animations on a single element, but what if you want to create more complex animations involving multiple elements? Sure, you could set up event listeners to listen for one animation to finish to start the next, but there’s a better way.

The WAAPI includes three objects that can be used to create more complex animation effects: KeyframeEffect, GroupEffect, and SequenceEffect. I’m only going to look at KeyframeEffect in this post, but know that it will be used with the other two later.

Unfortunately, as of June 2017, attempting to use KeyframeEffects puts us firmly in polyfill territory. It can be found on github or on npm.

Creating a KeyframeEffect

If you’re already familiar with the WAAPI, this section can be skipped. Otherwise, lets see how to make a keyframe effect.

To create the KeyframeEffect, we use the new KeyframeEffect constructor available in the browser. The syntax for creating one is as follows:

const theEffect = new KeyframeEffect(elementToAnimate, keyframes, timingOptions);

Then, to run it we will create a new animation using this effect. The animation constructor takes two arguments: the KeyframeEffect and a timeline. With the animation created, we can then use the animation methods (play, pause, reverse, etc.) to control playback.

const theAnimation = new Animation(theEffect, document.timeline);
theAnimation.play();

KeyframeEffect and React

Now that we know how to make a playable animation with KeyframeEffect, it’s pretty straightforward to make a basic animation in a react component.

import React, { Component } from 'react';
import ReactDOM from 'react-dom';
import 'web-animations-js/web-animations-next.min.js';

class AnimatedDiv extends Component {
    constructor() {
        super();
        this.toggleAnimation = this.toggleAnimation.bind(this);
    }

    componentDidMount() {
        if(!this.ref) return;

        let effect = new KeyframeEffect( this.ref, [{opacity:0},{opacity:1}], {duration: 500, fill:'both'} );
        
        this.animation = new Animation(effect, document.timeline);
        this.animation.play();
    }

    toggleAnimation() {
        if (this.animation) this.animation.reverse();
    }

    render() {
        return (
            <div style={{
                    height: 100,
                    width: 100, 
                    background: 'blue'
                }} 
                ref={(r) => this.ref=r}
                onClick={this.toggleAnimation}>
            </div>
        );
    }
}

ReactDOM.render(<AnimatedDiv />, document.getElementById('app'));

Easy, right? In the next post, we’ll look at how to play multiple KeyframeEffects at the same time using GroupEffects. See you then!