转载

《ReactJS》读书笔记十四

使用渐变组的隐患

使用渐变组也有两个隐患需要非常注意的。

首先,渐变组会延迟子组件的移除直到动画完成。也就是说如果你把一个列表的组件包裹进一个 ReactCSSTransitionGroup 中,却没有为 transitionName 属性指定的 class 明确任何 CSS,这些组件将永远无法被移除,甚至当你尝试不再渲染它们时也不可以。

其次,渐变组的每一个子组件都必须设置一个独一无二的 key 属性。渐变组使用这个属性来判断组件究竟是进人还是退出,因此如果没有设置 key 属性动画可能无法执行,同时组件也会变得无法移除。

注意,即使渐变组只有一个子元素,它也需要设置一个 key 属性。

间隔渲染

使用 CSS3 动画能够获得巨大的性能提升并拥有简洁的代码,但它们并不总是解决问题的正确工具。有些时候你必须要为较老的、不支持 CSS3 的浏览器做兼容,还有些时候你想为 CSS 属性之外的东西添加动画,比如滚动条位置或 Canvas 绘画。在这些情况下,间隔渲染能够满足我们的要求,但是相比 CSS3 动画来说,它会带来一定的性能损耗。

间隔渲染最基本的思想就是周期性地触发组件的状态更新,以明确当前处于整个动画时间中的什么阶段。通过在组件的 render() 方法中加入这个状态值,组件能够在每次状态更新触发的重渲染中正确表示当前的动画阶段。

因为这种方法涉及多次重渲染,所以通常最好和 requestAnimationFrame 一起使用以避免不必要的渲染。不过,在 requestAnimationFrame 不被支持或不可用的情况下,降级到不那么智能的 setTimeout() 就是唯一的选择了。

使用requestAnimationFrame实现间隔渲染

假设你希望使用间隔渲染将一个 div 从屏幕的一边移向另一边,可以通过给它添加 position: absolute 并随着时间变化不停更新 lefttop 属性来实现。根据消耗时间内的变化总量,用 requestAnimationFrame 来实现这个动画应该可以得出一个流畅的动画。

下面是具体实现的例子。

var Positioner = React.createClass({  getInitialState: function(){   return {    position: 0   };  },   resolveAnimationFrame: function(){   var timestamp = new Date();   var timeRemaining = Math.max(0, this.props.animationCompleteTimestamp - timestamp);   if (timeRemaining > 0){    this.setState({     position: timeRemaining    });   }  },   componentWillUpdate: function(){   if(this.props.animationCompleteTimestamp){    requestAnimationFrame(this.resolveAnimationFrame);   }  },   render: function(){   var divStyle = {    left: this.state.position   };   return <div style={divStyle}>This will animate!</div>  } }); 

在这个例子中,组件的 props 中设置了一个名为 animationCompleteTimestamp 的值,它和 requestAnimationFrame() 的回调中返回的时间戳一起被用来计算剩余多少位移。计算的结果存在 this.state.position 中,而 render() 方法会用它来确定 div 的位置。

由于 requestAnimationFramecomponentWillUpdate() 方法调用,所以只要组件的 props 有任何的变动(比如改变了 animationCompleteTimestamp )它就会被触发。它又包含了在 resolveAnimationFrame 中的 this.setState() 调用。这意味着一旦 animationComleteTime-stamp 被设置,组件就会自动调用后续的 requestAnimationFrame 方法,直到当前时间超过了 animationCompleteTimestamp 为止。

注意,这套逻辑只在基于时间戳的清况下成立。对 animationCompleteTimestamp 所做的改变会触发逻辑,而 this.state.position 的值完全依赖于当前时间与 animationCompleteTime-stamp 的差。正因如此, render() 方法可以自由地在各种动画中使用 this.state.position ,包括设置滚动条位置、在。 Canvas 上绘画,以及任何中间状态。

使用setTimeout实现间隔渲染

尽管 requestAnimationFrame 总体上能够以最小的性能损耗实现最流畅的动画,但它在较老的浏览器上是无法使用的,而且它被调用的次数可能比你想象的更频繁(也更加无法预)。在这些情况下你可以使用 setTimeout()

var Positioner = React.createClass({  getInitialState: function(){   return {    position: 0   };  },   resolveSetTimeout: function(){   var timestamp = new Date();   var timeRemaining = Math.max(0, this.props.animationCompleteTimestamp - timestamp);   if (timeRemaining > 0){    this.setState({     position: timeRemaining    });   }  },   componentWillUpdate: function(){   if (this.props.animationCompleteTimestamp){    setTimeout(this.resolveSetTimeout, this.props.timeoutMs);   }  },   render: function(){   var divStyle = {    left: this.state.position   };   return <div style={divStyle}>This will animate!</div>  } }); 

由于 setTimeout() 接受一个显式的时间间隔,而 requestAnimationFrame 是自己来决定这个时间间隔的,因此这个组件需要额外依赖一个变量 this.props.timeoutMs ,以此来明确要使用的间隔。

总结

使用这些动画技术,你现在可以:

  1. 在状态改变过程中,使用 CSS3 和渐变组高效地应用渐变动画。
  2. 使用 requestAnimationFrame 为 CSS 之外的东西添加动画,如滚动条位置或 Canvas 绘画。
  3. requestAnimationFrame 不被支持时降级到 setTimeout() 方法。
原文  http://justclear.github.io/developing-a-react-edge-notes-for-part-14
正文到此结束
Loading...