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