react動畫難寫?試試react版transformjs


簡介

transformjs在非react領域用得風生水起,那么react技術棧的同學能用上嗎?答案是可以的。junexie童鞋已經造了個react版本

動畫實現方式

傳統 web 動畫的兩種方式

  1. 純粹的CSS3 :如:transition/animation+transform(大名鼎鼎的animate.css)
  2. JS + CSS3 transition或者animation:這里第一種一樣,只是通過js里add class和remove class去增加或者移除對應的動畫
  3. 純粹JS控制時間軸:第一和第二種都是自帶時間軸,使用 setInterval / setTimeout / requestAnimationFrame 不斷地修改 DOM 的 style 屬性產生動畫

對應在react中

使用CSS3

  • 使用 ReactCSSTransitionGroup 來實現
  • 相關動畫的class都有對應的state,修改state相當於增加或者移除class,也就相當於js里add class和remove class去增加或者移除對應的動畫

純粹JS控制時間軸

  • 仍然使用 setInterval / setTimeout / requestAnimationFrame,修改某個 state 值,然后映射到 component 的 style 上。

這里很明顯,方案1和方案2可應對簡單場景(如沒有prop change 回調等),方案3可編程性最大,最靈活,可以適合復雜的動畫場景或者承受復雜的交互場景。

安裝

npm install css3transform-react

API

//set "translateX", "translateY", "translateZ", "scaleX", "scaleY", "scaleZ", "rotateX", "rotateY", "rotateZ", "skewX", "skewY", "originX", "originY", "originZ"
render() {
    return (
        <Transform
          translateX={100}
          scaleX={0.5}
          originX={0.5}>
          <div>sth</div>
        </Transform>
    );
}

// you can also use other porps, such as "className" or "style"
render() {
    return (
        <Transform
          translateX={100}
          className="ani"
          style={{width: '100px', background: 'red'}}
          <div>sth</div>
        </Transform>
    );
}

通過上面的聲明,就可以設置或者讀取"translateX", "translateY", "translateZ", "scaleX", "scaleY", "scaleZ", "rotateX", "rotateY", "rotateZ", "skewX", "skewY", "originX", "originY", "originZ"!

方便吧!

使用姿勢

import React, { Component } from 'react';
import { render } from 'react-dom';

import Transform from '../../transform.react.js';

class Root extends Component {

  constructor(props, context) {
    super(props, context);

    this.state = {
      el1: {rotateZ: 0},
      el2: {rotateY: 0}
    };

    this.animate = this.animate.bind(this);
  }

  animate() {
    this.setState({
      el1: {rotateZ: this.state.el1.rotateZ + 1},
      el2: {rotateY: this.state.el2.rotateY + 1}
    }, () => {
      requestAnimationFrame(this.animate);
    });

  }

  componentDidMount() {
    setTimeout(this.animate, 500);
  }

  render() {
    return (
      <div>
        <Transform rotateZ={this.state.el1.rotateZ} className="test" style={{'backgroundColor': 'green'}}>
          transformjs
        </Transform>

        <Transform rotateY={this.state.el2.rotateY} className="test" style={{'backgroundColor': 'red', 'left': '200px'}}>
          transformjs
        </Transform>

      </div>
    );
  }
}

render(
	<Root />,
	document.getElementById('root')
);

更加復雜的詳細的使用代碼可以看這里:https://github.com/AlloyTeam/AlloyTouch/blob/master/transformjs/react/example/src/index.jsx

在線演示

http://alloyteam.github.io/AlloyTouch/transformjs/react/example/

性能對比

因為react版本會有diff過程,然后apply diff to dom的過程,state改變不會整個innerHTML全部替換,所以對瀏覽器渲染來說還是很便宜,但是在js里diff的過程的耗時還是需要去profiles一把,如果耗時嚴重,不在webworker里跑還是會卡住UI線程導致卡頓,交互延緩等。所以要看一看CPU的耗時還是很有必要的。
主要是那上面的演示和傳統的直接操作dom的方式對比。就是下面這種傳統的方式:

var element1 = document.querySelector("#test1");
Transform(element1);
...
...
function animate() {
    element1.rotateZ++;
    ...
    requestAnimationFrame(animate);
}

animate();

對兩種方式使用chrome profiles了一把。
先看總耗時對比

react:

傳統方式:

  • react在8739秒內CPU耗時花費了近似1686ms
  • 傳統方式在9254ms秒內CPU耗時花費近似700ms

在不進行profiles就能想象到react是一定會更慢一些,因為state的改變要走把react生命周期走一遍,但是可以看到react的耗時還是在可以接受的范圍。但是,我們還是希望找到拖慢的函數來。
那么在使用transformjs react版本中,哪個函數拖了后腿?展開profiles tree可以看到:

就是它了。

/**
	   * Reconciles the properties by detecting differences in property values and
	   * updating the DOM as necessary. This function is probably the single most
	   * critical path for performance optimization.
	   *
	   * TODO: Benchmark whether checking for changed values in memory actually
	   *       improves performance (especially statically positioned elements).
	   * TODO: Benchmark the effects of putting this at the top since 99% of props
	   *       do not change for a given reconciliation.
	   * TODO: Benchmark areas that can be improved with caching.
	   *
	   * @private
	   * @param {object} lastProps
	   * @param {object} nextProps
	   * @param {?DOMElement} node
	   */
	  _updateDOMProperties: function (lastProps, nextProps, transaction) {

打開對應的代碼可以看到。注釋里已經寫了這是優化重點。

開始使用吧

官方網站:http://alloyteam.github.io/AlloyTouch/transformjs/

Github地址:https://github.com/AlloyTeam/AlloyTouch/tree/master/transformjs
任何問題和意見歡迎new issue給我們。


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM