React.js: Non-CSS animations

animation, css, javascript, reactjs, svg

Solution

I successfully used this project in my react-hammer integration project there are some examples with hammer events and react animation.

Here code of animated "BlinkingThing":

var BlinkingThing = React.createClass({
    mixins: [React.Animate],
    blink: function () {
        var that = this;
        var animateAfter = function () {
            that.animate({
                color: 'green'
            }, that.props.blinkBack);
        };
        this.animate({
            color: 'yellow'
        }, this.props.blinkTo, animateAfter);
    },
    componentDidReceiveProps: function () {
        this.setState({color: this.props.color})
    },
    componentDidMount: function () {
        this.setState({color: this.props.color})
    },
    receiveHammerEvent: function (ev) {
        if (ev) {
            var value = ev.type;

            switch (value) {
                case 'tap':
                    this.blink();
                    break;
            }
        }
    },
    getInitialState: function () {
        return {};
    },
    render: function () {
        var style = {
            display: 'inline-block',
            backgroundColor: this.state.color
        };

        return (<div style={style}>{this.props.children}</div>);
    }
});

You need some parent component which will propagate side effects(touch events, for example) to trigger changes of state in BlinkingThing component (animation rely on state changing when you call this.animate func), I made a HitArea component to do that. It calls receiveHammerEvent func from its children passing hammer event when it occurs.

Problem

React documentation doesn’t have anything about handling animations that are not CSS transitions, such as animation of scroll position and SVG attributes. As for CSS transitions, there is an add-on. Here is a simple SVG example example: ``` /** * @jsx React.DOM */ function animate(duration, onStep) { var start = Date.now(); var timer = {id: 0}; (function loop() { timer.id = requestAnimationFrame(function() { var diff = Date.now() - start; var fraction = diff / duration; onStep(fraction); if (diff < duration) { loop(); } }); })(); return timer; } function lerp(low, high, fraction) { return low + (high - low) * fraction; } var App = React.createClass({ getInitialState: function() { return {x: 0} }, move: function(i) { this.setState({x: this.state.x + i * 100}); }, render: function() { return <div className="ShowerItem"> <p> <button onClick={this.move.bind(this, -1)}>Left</button> <button onClick={this.move.bind(this, 1)}>Right</button> </p> <svg><Dot x={this.state.x}/></svg> </div>; } }); var Dot = React.createClass({ getInitialState: function() { return { x: 0, final: 0 }; }, timer: null, render: function() { var from = this.state.x; var to = this.props.x; if (to !== this.state.final) { this.state.final = to; if (this.timer) { cancelAnimationFrame(this.timer.id); } this.timer = animate(500, function(fraction) { var x = lerp(from, to, fraction); if (fraction >= 1) { this.setState({ value: to }); this.timer = null; } else { this.setState({x: x}); } }.bind(this)) } return <circle r="10" cy="10" cx={this.state.x + 10}/> } }); React.renderComponent( <App/>, document.body ); ``` Is there a more efficient way of doing animations? It the code architecture right? CSS Transitions add-on doesn't help here since I don't use CSS.

Original source