简体   繁体   English

React.js:非CSS动画

[英]React.js: Non-CSS animations

React documentation doesn't have anything about handling animations that are not CSS transitions, such as animation of scroll position and SVG attributes. React文档没有关于处理非CSS转换的动画的任何内容,例如滚动位置和SVG属性的动画。

As for CSS transitions, there is an add-on . 至于CSS过渡,有一个附加组件

Here is a simple SVG example example : 这是一个简单的SVG示例示例

动画SVG圈

/**
 * @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. CSS Transitions附加组件在这里没有帮助,因为我不使用CSS。

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": 这里是动画“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. 你需要一些传播副作用的父组件(例如触摸事件)来触发BlinkingThing组件中的状态变化(当你调用this.animate func时动画依赖于状态变化),我做了一个HitArea组件来做到这一点。 It calls receiveHammerEvent func from its children passing hammer event when it occurs. 当它发生时,它会从其子节点调用receiveHammerEvent函数传递锤子事件。

Been having the same exact problems myself, until recently I've found out about Rekapi. 我自己一直有同样的问题,直到最近我才发现了Rekapi。 This library provides state-based animations tools. 该库提供基于状态的动画工具。 Check out the tutorial https://github.com/jeremyckahn/rekapi/blob/master/docs/getting_started.md 查看教程https://github.com/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, ie a component 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 component(context), or simply write a "one trick actor" that always forwards its state to the component each frame. 诀窍是,上下文不必是canvas或DOM元素,它可以是普通对象,即组件实例或mixin,因此这可以在actor的render方法中执行某些逻辑,然后在你的组件(上下文)上设置setState,或者简单地写一个“一个技巧演员”,它总是将每个帧的状态转发给组件。

It seems react.animate is an actively maintained and often used React animation libraries. 似乎react.animate是一个积极维护并经常使用的React动画库。

(it was already mentioned above, but would be easy to miss in all the links) (上面已经提到过,但很容易在所有链接中遗漏)

Note: it comes with/requires underscore.js. 注意:它附带/需要underscore.js。

This is what I came up so far: http://jsfiddle.net/NV/NtP7n/ . 这就是我到目前为止所提出的: http//jsfiddle.net/NV/NtP7n/

I rewrote Dot to leverage React's draw loop: 我重写了Dot以利用React的绘制循环:

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. 我必须使用setTimeout,因为在render方法中不允许使用setState。 I wonder if I can queue an update on the next tick without using setTimeout. 我想知道是否可以在不使用setTimeout的情况下在下一个刻度上排队更新。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM