最新消息:雨落星辰是一个专注网站SEO优化、网站SEO诊断、搜索引擎研究、网络营销推广、网站策划运营及站长类的自媒体原创博客

javascript - React.js: Non-CSS animations - Stack Overflow

programmeradmin6浏览0评论

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.

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.

Share Improve this question edited Jun 17, 2014 at 1:10 NVI asked May 28, 2014 at 8:37 NVINVI 15k16 gold badges68 silver badges104 bronze badges 7
  • This question is not about jQuery, and CSS wouldn’t do anything for the provided SVG example. – NVI Commented May 28, 2014 at 9:02
  • How does it look in the Chrome Timeline tab on your puter? For mine, a typical frame is: 0.503 ms Scripting, 0.070 ms Rendering, 0.821 ms Painting, 0.782 ms Other, 14.007 ms Idle and the animation is smooth. – Douglas Commented May 28, 2014 at 9:55
  • 2 The animation is also quite smooth for me. You may also be interested in this demo (it's the one Pete Hunt shows off in the JSConf.Asia talk "Rethinking Best Practices" at 29:30) that does animations via JavaScript. – Michelle Tilley Commented May 28, 2014 at 16:08
  • 3 The demo code is at github./petehunt/react-touch, but all the good stuff is in github./petehunt/react-touch-lib. Another one you might want to look at is github./petehunt/react-raf-batching. – Robin Luiten Commented May 30, 2014 at 3:08
  • 2 You can check out github./aino/ainojs-animation that provides a simple animation interface with a frame callback that can be used to set states. There is a simple React example in the README. – David Hellsing Commented Jul 9, 2014 at 8:07
 |  Show 2 more ments

4 Answers 4

Reset to default 2

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);
    },
    ponentDidReceiveProps: function () {
        this.setState({color: this.props.color})
    },
    ponentDidMount: 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 ponent which will propagate side effects(touch events, for example) to trigger changes of state in BlinkingThing ponent (animation rely on state changing when you call this.animate func), I made a HitArea ponent to do that. It calls receiveHammerEvent func from its children passing hammer event when it occurs.

Been having the same exact problems myself, until recently I've found out about Rekapi. This library provides state-based animations tools. Check out the tutorial https://github./jeremyckahn/rekapi/blob/master/docs/getting_started.md

The trick is, the context doesn't have to be a canvas or a DOM element, it can be a plain object, i.e. a ponent instance or a mixin, so this opens the possibility either to perform some logic in your actor's render method and then setState on your ponent(context), or simply write a "one trick actor" that always forwards its state to the ponent each frame.

It seems react.animate is an actively maintained and often used React animation libraries.

(it was already mentioned above, but would be easy to miss in all the links)

Note: it es with/requires underscore.js.

This is what I came up so far: http://jsfiddle/NV/NtP7n/.

I rewrote Dot to leverage React’s draw loop:

var Dot = React.createClass({

    getInitialState: function() {
        return {
            start: 0,
            x: 0,
            final: 0,
            startTime: 0
        };
    },

    render: function() {
        var state = this.state;
        var props = this.props;
        var amount = 0;

        if (state.final !== props.x) {
            state.final = props.x;
            state.start = state.x;
            state.startTime = Date.now();
        } else {
            amount = (Date.now() - state.startTime) / this.props.duration;
        }

        if (amount <= 1) {
            var x = state.start + amount * (props.x - state.start);
            setTimeout(function() {
                this.setState({x: x});
            }.bind(this), 1);
        } else {
            state.final = state.x = x = props.x;
        }

        return <circle r="10" cy="10" cx={x + 10}/>
    }
});

I have to call:

setTimeout(function() {
    this.setState({current: x});
}.bind(this), 1);

just to force update on the next tick. I have to use setTimeout because setState isn’t allowed in render method. I wonder if I can queue an update on the next tick without using setTimeout.

发布评论

评论列表(0)

  1. 暂无评论