React.js:非 CSS 动画

Posted

技术标签:

【中文标题】React.js:非 CSS 动画【英文标题】:React.js: Non-CSS animations 【发布时间】:2014-07-17 09:08:03 【问题描述】:

React documentation 没有任何关于处理非 CSS 过渡动画的内容,例如滚动位置动画和 SVG 属性。

至于 CSS 过渡,有an add-on。

这里是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
);

有没有更有效的制作动画的方法? 代码架构对吗?

CSS Transitions add-on 在这里没有帮助,因为我不使用 CSS。

【问题讨论】:

这个问题与 jQuery 无关,CSS 不会为提供的 SVG 示例做任何事情。 它在您计算机上的 Chrome 时间轴选项卡中的外观如何?对于我来说,一个典型的帧是:0.503 ms Scripting, 0.070 ms Rendering, 0.821 ms Painting, 0.782 ms Other, 14.007 ms Idle,并且动画很流畅。 动画对我来说也很流畅。您可能还对通过 javascript 制作动画的 this demo(这是 Pete Hunt 在 29:30 在 JSConf.Asia talk "Rethinking Best Practices" 中展示的那个)感兴趣。 演示代码在github.com/petehunt/react-touch,但所有好东西都在github.com/petehunt/react-touch-lib。另一个你可能想看的是github.com/petehunt/react-raf-batching。 您可以查看github.com/aino/ainojs-animation,它提供了一个简单的动画界面和一个frame 回调,可用于设置状态。 README 中有一个简单的 React 示例。 【参考方案1】:

这是我目前想到的:http://jsfiddle.net/NV/NtP7n/。

我重写了 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/>
    
);

我必须打电话:

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

只是为了在下一个刻度上强制更新。我必须使用 setTimeout,因为 render 方法中不允许使用 setState。我想知道我是否可以在不使用 setTimeout 的情况下在下一个滴答时排队更新。

【讨论】:

您可能可以挂钩到 componentDidUpdate 以将下一个状态更改排队。我这样做不小心触发了无限的css转换循环:)【参考方案2】:

我在我的 react-hammer 集成中成功使用了这个 project project 有一些锤子事件和反应动画的例子。

这里是动画“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>);
    
);

您需要一些会传播副作用(例如触摸事件)的父组件来触发 BlinkingThing 组件中的状态更改(当您调用 this.animate func 时动画依赖于状态更改),我制作了一个 HitArea 组件来做那。它在发生时从传递锤子事件的子代中调用 receiveHammerEvent 函数。

【讨论】:

【参考方案3】:

我自己也遇到了同样的问题,直到最近我才发现了 Rekapi。 该库提供基于状态的动画工具。查看教程https://github.com/jeremyckahn/rekapi/blob/master/docs/getting_started.md

诀窍是,上下文不必是画布或 DOM 元素,它可以是普通对象,即组件实例或 mixin,因此这为在你的 actor 中执行某些逻辑提供了可能性render 方法,然后在你的组件(上下文)上设置状态,或者简单地编写一个“一个技巧演员”,它总是将其状态转发给每一帧的组件。

【讨论】:

【参考方案4】:

似乎react.animate 是一个积极维护且经常使用的 React 动画库。

(上面已经提到过,但是在所有链接中很容易错过)

注意:它带有/需要 underscore.js。

【讨论】:

以上是关于React.js:非 CSS 动画的主要内容,如果未能解决你的问题,请参考以下文章

React.js扭曲相册焦点图动画

使用 React.js 为自定义轮播设置动画

如何使用 React Native 创建发光动画

使用adaptiveHeight时React.js动画幻灯片高度

使用 React JS 制作动画

什么是对列表重新排序进行动画处理的 react.js 友好方式?