Flipboard推出React Canvas

15-02-11 banq
                   

Flipboard增加React组件渲染<canvas>,而不是DOM。该开源项目不少代码已经进入flipboard.com的生产阶段:Flipboard/react-canvas · GitHub

Facebook刚刚发布React.js是基于DOM渲染,通过算法计算虚拟DOM和实际物理DOM之间的差别,然后渲染差别达到快速性能。参考:使用React Native的第一印象,React.js单方向的数据流和声明API已经改变了人们构建应用的方式:flux模式

如果将React组件绑定到canvas布局(而不是DOM)会怎样呢?Flipboard进行了大胆尝试:

Flipboard已经有很长的构建面向移动设备的界面历史,他们发现移动应用相对原生应用感觉比较慢的是DOM,CSS动画和切换是在Web上最快速实现平滑动画的方式,但是它们有一些限制,而React Canvas考虑到大多数现代浏览器已经有硬件加速的canvas。

同时还有其他试图绑定Canvas绘图API实现React,它们更加注重在视觉效果和游戏。在构建不同用户界面上的重点不同导致React Canvas的差别,事实上,渲染到canvas是一个实现细节。

React Canvas给Web开发者带来熟悉的API,并带给他们一个高性能绘图引擎。

以使用Javascript实现页面滚动效果来说明,使用一个canvas元素实现滚动,当发生每个触摸事件时,当前渲染树被当前根据滚动偏移量计算的每个节点更新,整个渲染树然后重新使用新的坐标系渲染。这个过程会很慢,但是这里有一个重要的性能优化:可将绘图操作结果缓存在一个off-screen离屏canvas上,这个off-screen离屏canvas然后在稍后时间用来重画。

这种技术不但可用于图片层,文字和图形都可以,很费力的绘图操作是填充文字和图形图像,但是一旦等到这些层画完,能够使用off-screen离屏canvas快速重画它们。下图每个页面分为两个层,一个图片层和一个文字层,文字层包含很多组元素,在滚动动画的每一帧中,两个层使用缓存位图bitmap进行重画:

使用React Canvas的实现代码:

var ListView = React.createClass({
  getInitialState: function () {
    return {
      scrollTop: 0
    };
  },

  render: function () {
    var items = this.getVisibleItemIndexes().map(this.renderItem);
    return (
      <Group
        onTouchStart={this.handleTouchStart}
        onTouchMove={this.handleTouchMove}
        onTouchEnd={this.handleTouchEnd}
        onTouchCancel={this.handleTouchEnd}>
        {items}
      </Group>
    );
  },

  renderItem: function (itemIndex) {
    // Wrap each item in a <Group> which is translated up/down based on
    // the current scroll offset.
    var translateY = (itemIndex * itemHeight) - this.state.scrollTop;
    var style = { translateY: translateY };
    return (
      <Group style={style} key={itemIndex}>
        <Item />
      </Group>
    );
  },

  getVisibleItemIndexes: function () {
    // Compute the visible item indexes based on `this.state.scrollTop`.
  }
});
<p>

触摸事件:

// Create the Scroller instance on mount.
componentDidMount: function () {
  this.scroller = new Scroller(this.handleScroll);
},

// This is called by the Scroller at each scroll event.
handleScroll: function (left, top) {
  this.setState({ scrollTop: top });
},

handleTouchStart: function (e) {
  this.scroller.doTouchStart(e.touches, e.timeStamp);
},

handleTouchMove: function (e) {
  e.preventDefault();
  this.scroller.doTouchMove(e.touches, e.timeStamp, e.scale);
},

handleTouchEnd: function (e) {
  this.scroller.doTouchEnd(e.timeStamp);
}

<p>

这么简单的代码体现了React的最好品质,触摸事件被声明地绑定在render()中,每个触摸事件转发到Scroller,在那里计算当前滚动距离顶部的偏移差值,从Scroller发出的每个滚动事件更新ListView组件的状态,只是更新屏幕上可见部分,所有这些在16毫秒内发生,因为React的比较算法非常快。

React Canvas相比iOS/Andriod原生应用的优点,比如原生应用中不能从应用程序中复制一段文本,或者复制一个链接到浏览器,复制电话号码或地址。而基于DOM/Canvas能做到这些。

[该贴被banq于2015-02-11 17:13修改过]

                   

1