閱讀172 返回首頁    go 阿裏雲 go 技術社區[雲棲]


React動畫框架簡介

由於 React 加持了虛擬 DOM 等諸多特性,所以在 React 上實現常規的動畫效果有一些特別之處。本文不會深入探討 React 對動畫的處理邏輯,隻會簡單地演示如何使用 React 創建動畫效果。

React 插件

React 官方提供了兩個插件用於處理動畫效果:一個是偏底層的 react-addons-transition-group,一個是在前者基礎上進一步封裝的 react-addons-css-transition-group。在使用它們之前,需要先檢查下你使用的是哪種類型的 React 版本,一般通過 npm 安裝的 React 默認不會安裝這兩個插件,需要手動安裝它們:

npm install --save react-addons-transition-group 
react-addons-css-transition-group

在這裏先介紹 react-addons-css-transition-group 的使用方式,使用它可以快速利用 CSS 的能力實現組件的入場和出場動畫。使用該插件實現 React 動畫需要兩個部分的協作,首先是 JS 部分的組件:

// 非完整代碼
class App extends React.Component {
    state = {
        itemList: [{
            key: getRandomKey(),
            color: colorSet[getRandomIndex(0, 10)]
        }]
    };
    render () {
        const { itemList } = this.state;
        let itemNodeList = [];
        itemList.forEach((item) => {
            itemNodeList.push(
                <div
                    // 重點:CSSTransitionGroup 的直接子組件必須有一個獨一無二的 key 值
                 key={item.key}
                    className="item"
                    style={{ backgroundColor: `${item.color}` }}
                    onClick={() => this.handleRemove(item.key)}>
                    {item.key}
                </div>
            );
        });
        return (
            <div >
                <button onClick={this.handleAdd}>ADD</button>
              <CSSTransitionGroup
                 component="div"
                 className="color-set"
                transitionName="todo"
                transitionAppear={true}
                 transitionAppearTimeout={500}
                 transitionEnterTimeout={500}                          transitionLeaveTimeout={300}>
                  {itemNodeList}
              </CSSTransitionGroup>
            </div>
        );
    }
}

在 CSSTransitionGroup 組件上,我們聲明了一堆以 transition 開頭的屬性,這些屬性被用來控製動畫效果:

  • transitionName,寫樣式時的前綴,比如這裏的值為 todo,那麼 CSS 的類型就應該是todo-enter、todo-leave 等等
  • transitionAppear,布爾值,是否在所有組件掛載時觸發動畫
  • transitionEnterTimeout,控製入場動畫的時長
  • transitionLeaveTimeout,控製退場動畫的時長
  • transitionAppearTimeout,控製所有組件掛載的動畫時長

默認情況下,CSSTransitionGroup 組件會被渲染為 span 標簽,如果你想修改標簽類型,可以使用 component 屬性進行修改。其次是 CSS 部分的樣式,CSS 中類選擇器遵循類似 ${transitionName}-appear 的命名格式:

.todo-appear {
    opacity: 0.01;
    transform: translateY(-50px);
}
.todo-appear.todo-appear-active {
    opacity: 1;
    transform: translateY(0px);
    transition: all 500ms ease-in;
}
.todo-enter {
    opacity: 0.01;
    transform: translateY(-50px);
}
.todo-enter.todo-enter-active {
    opacity: 1;
    transform: translateY(0px);
    transition: all 500ms ease-in;
}
.todo-leave {
    opacity: 1;
    transform: translateY(0px);
}
.todo-leave.todo-leave-active {
    opacity: 0.01;
    transform: translateY(-50px);
    transition: all 300ms ease-in;
}

通過上述兩部分的結合,當我們刪除 itemNodeList 中的某個組件時,React 會立即通過 key 找到這個組件,然後為其添加 todo-leave 類名,並瞬間添加 todo-leave-active 類名,在 500 毫秒之後移出該組件。

這種動畫處理方式的優點如下:

  • 簡單快速,與 React 的融合性好,性能有保障
  • 可以使用 Sass、Less 等預處理器,提高開發效率
  • 易於上手,無第三方依賴,也就是無門的動畫處理模塊,這裏的插件隻是將類選擇器應用到相關的節點上

不過缺點也很多:

  • 隻有入場和出場動畫,無法實現複雜動畫
  • 組件之間的動畫效果是獨立的,無互動,動畫效果生硬 要求和限製條件多
  • 使用 CSS Modules 需要硬編碼,也就是使用CSSTransitionGroup 組件自定義類名的功能

最後,列出使用 React 插件開發動畫的幾點要求:

  • 組件必須掛載才能實現動畫
  • 組件必須設定獨一無二的 key 值
  • transitionName 必須與 CSS 中的樣式類名保持一致

從上麵的示例可以看出,CSSTransitionGroup 組件主要用來在組件入場和出場時給 DOM 節點添加類名,相當於是與 CSS 的結合,那麼我們是否能夠通過 JS 生成行內樣式,然後添加到 DOM 節點,實現更加靈活的動畫效果呢?答案是可以的,React 提供了 ReactTransitionGroup 組件供開發者在以下六個階段向 DOM 節點注入數據:

  • componentWillAppear(callback)
  • componentDidAppear()
  • componentWillEnter(callback)
  • componentDidEnter()
  • componentWillLeave(callback)
  • componentDidLeave()

React Motion

使用react-motion之前需要先安裝庫,安裝命令如下:

npm install --save react-motion

這裏寫圖片描述

對於絕大多數的動畫組件,我們往往不希望對動畫屬性(寬高、顏色等)的變化時間做硬編碼處理,react-motion 提供的 spring 函數就是用來解決這一需求的,它可以逼真地模仿真實的物理效果,也就是我們常見的各類緩動效果。react-motion 一共提供了五個 API 接口,其中前兩個是輔助類函數,後三個是具體的動畫組件:

  • spring,聲明動畫的緩動效果,比如 spring(10, {stiffness: 120, damping: 17}),10是目標值,stiffness 是彈性動畫的剛度值,影響彈性,damping 是彈性動畫的阻尼;
  • presets,預置的緩動效果,比如 spring(10, preset.gentle);
  • Motion,該動畫組件內部往往隻有一個直接子組件,也就是隻有一個動畫目標;
  • StaggerdMotion,該動畫組件內部有一個或多個直接子組件,多個子組件之間的動畫效果由關聯性;
  • TransitionMotion,該動畫組件內部的一個或多個組件可以卸載或掛載,提供 Enter 和 Leave 動畫效果;

示例代碼如下:

<Motion 
    defaultStyle={{x: 0}} 
    style={{x: spring(10)}}
    onRest={() => void}>
    {
        interpolatingStyle => (
            <div style={interpolatingStyle}>{interpolatingStyle.x}</div>
        )
    }
</Motion>

上麵代碼演示了 Motion 組件的最基礎使用方法。

  • defaultStyle?: PlainStyle,可選參數,PlainStyle 指的就是 React 常用作行內樣式的對象類型的 {width: '10px', height: '10px' },見名知意,為動畫設定初始值
  • style: Style,必選參數,指定動畫完成的目標值,並設定動畫的變化類型,實際上是一種數據驅動的形式
  • onRest?: () => void,可選參數,在動畫完成後調用
  • children: (interpolatedStyle: PlainStyle) =>ReactElement,必選函數,接收一個從初始值到目標值中間的值,這個值不斷變化,用於渲染子組件的樣式

關於 StaggerdMotion 組件和 TransitionMotion 組件這裏就不多做介紹了,有興趣的話請閱讀官方文檔

GSAP

GSAP 是一個老牌的專業級動畫庫,從古老的 Flash 動畫時代一直興盛至今,它是一個商業產品,雖然開發者可以免費下載源代碼,但如果要在商業活動中使用它,請購買相關的會員。如果你沒有使用過 GSAP,建議閱讀GSAP,接下來,我們嚐試將 GSAP 融入到 React 的開發中。

將 GSAP 與 React 結合最簡單的方式是,使用 ref 或 findDOMNode()。例如:

// 導入通過 NPM 安裝的 GSAP 
import TweenMax from 'gsap';
// 保存 ref 指向的真實節點
let refNode;
class App extends React.Component {
    componentDidMount () {
        TweenMax.to(refNode, 2, {
            x: '+=200px',
            backgroundColor: '#2196f3'
        });
        // TweenMax 可以做什麼?
        // 暫停
        tween.pause();
        // 繼續播放
        tween.resume();
        // 反轉播放
        tween.reverse();
        // 跳轉到1s進度處開始播放
        tween.seek(1);
        // 重播
        tween.restart();
        // 動畫變為三倍速
        tween.timeScale(3);
    }
    render () {
        return (
            <div
                
                ref={c => (refNode = c)}
                style={{
                    width: '100px',
                    height: '100px',
                    margin: '100px',
                    borderRadius: '50%',
                    backgroundColor: 'red'
                }}>
            </div>
        );
    }
}

TweenMax 的強大在於多年的動畫開發經驗所積累的技術底蘊,非一朝一夕可以被替代,上麵簡單演示了 TweenMax 的一小部分功能,像是 Timeline 的用法就沒有介紹,當需要組織的元素越來越多時,其靈活性就會越加凸顯。

這裏寫圖片描述

GSAP 與 React 結合的另一種方式是使用 ReactTransitionGroup 組件,ReactTransitionGroup 組件提供了六個生命周期方法:

  • componentWillAppear(callback)
  • componnetDidAppear()
  • componentWillEnter(callback)
  • componnetDidEnter()
  • componentWillLeave(callback)
  • componnetDidLeave()

示例:

class Box extends React.Component {
    componentWillEnter (callback) {
        const el = ReactDOM.findDOMNode(this);
        TweenMax.fromTo(el, 0.3, {y: 100, opacity: 0}, {y: 0, opacity: 1, onComplete: callback});
    }
    componentWillLeave (callback) {
        const el = ReactDOM.findDOMNode(this);
        TweenMax.fromTo(el, 0.3, {y: 0, opacity: 1}, {y: -100, opacity: 0, onComplete: callback});
    }
    render () {
        return (
            <div
                style={{
                    width: '100px',
                    height: '100px',
                    margin: '100px',
                    borderRadius: '50%',
                    backgroundColor: 'red'
                }}>
            </div>
        );
    }
}
class App extends React.Component {
    state = {
        show: true
    }
    handleToggle = () => {
        this.setState({
            show: !this.state.show
        });
    }
    render () {
        return (
            <div className="wrapper">
                <button onClick={this.handleToggle}>Toggle</button>
                <TransitionGroup>
                    { this.state.show && <Box key="a"></Box> }
                </TransitionGroup>
            </div>
        );
    }
}

總結

對於上麵提到的幾個動畫框架,做一個簡單的對比:

  • 易用性:CSSTransitionGroup >= React Motion > GSAP
  • 可維護性:看代碼量和技術能力,CSSTransitionGroup 最簡單
  • 用戶體驗:GSAP >= React Motion > CSSTransitionGroup
  • 對複雜動畫的支持程度:GSAP > React Motion > CSSTransitionGroup

這裏寫圖片描述

參考資料
React Animation
React Motion
A Comparison of Animation Technologies
GSAP,專業的Web動畫庫
React GSAP Enhancer
Animations with ReactTransitionGroup
GSAP Examples

最後更新:2017-09-27 21:03:12

  上一篇:go  一張圖掌握移動Web前端所有技術(大前端、工程化、預編譯、自動化)
  下一篇:go