背景:
在當(dāng)下的前端背景下,前端的頁面對于用戶來說康愤,不僅僅是實(shí)現(xiàn)了功能,更要注重頁面的表現(xiàn)形式舶吗,來提升用戶的體驗(yàn)度征冷。React 作為最近幾年比較流行的前端開發(fā)框架,提出了虛擬 DOM 概念誓琼,所有 DOM 的變化都先發(fā)生在虛擬 DOM 上检激,通過 DOM diff 來分析網(wǎng)頁的實(shí)際變化,然后反映在真實(shí) DOM 上腹侣,從而極大地提升網(wǎng)頁性能叔收。并且react強(qiáng)調(diào)用數(shù)據(jù)去驅(qū)動頁面的展示,并不直接去操作DOM傲隶,從而在react中添加動畫就變得有點(diǎn)困難饺律,今天就介紹幾種在react中添加動畫的方式。
1.基于 React 組件狀態(tài)的 CSS 動畫
class App extends Component {
this.state = {
switch: false
}
componentDidMount() {
this.input.addEventListener('focus', this.focus);
this.input.addEventListener('blur', this.focus);
}
focus = () => {
this.setState((state) => ({ switch: !state.switch}))
}
render() {
return (
<div className="App">
<div className="container">
<input
ref={input => this.input = input}
className={['input', this.state.switch&& 'input-switch'].join(' ')}
/>
</div>
</div>
);
}
}
- 我們通過一個狀態(tài)switch去控制input元素上的類名跺株,每個類名都加了css3動畫复濒。
2.在componentDidMount中去監(jiān)聽input元素獲取焦點(diǎn)和失去焦點(diǎn)時控制switch的變化,從而控制元素動畫的展現(xiàn)形式乒省。
2.React Motion
一個簡單的應(yīng)用:
<Motion defaultStyle={{left: 0}} style={{left: spring(10)}}>
{interpolatingStyle => <div style={interpolatingStyle} />}
</Motion>
指定一個初始style(defaultStyle)芝薇,然后賦值一個目標(biāo)style(style),中間每幀都會由react-motion計算出對應(yīng)的style作儿,用戶只管使用生成的style(interpolatingStyle)洛二,不用關(guān)心物理效果的實(shí)現(xiàn),動畫中斷的處理,一切事情都交給react-motion晾嘶。
詳情可參考:https://github.com/chenglou/react-motion
3.react-addons-css-transition-group
react-addons-css-transition-group插件妓雾,是利用css的transition和animation實(shí)現(xiàn)組件的進(jìn)場和出場動畫的。ReactCSSTransitionGroup是在ReactTransitionGroup的基礎(chǔ)上進(jìn)行再封裝垒迂。
import React, {PropTypes} from 'react';
import CSSTransitionGroup from 'react-addons-css-transition-group';
/* 定義參數(shù)類型 */
const propTypes = {
imageSrc: PropTypes.array.isRequired,
currentIndex: PropTypes.number.isRequired,
enterDelay: PropTypes.number.isRequired,
leaveDelay: PropTypes.number.isRequired,
name: PropTypes.string.isRequired,
component: PropTypes.string.isRequired
}
/* 輪播圖片組件簿透,無狀態(tài)組件 */
function CarouselImage(props) {
/* 對象解析亡问,參數(shù)分別對應(yīng):圖片地址數(shù)組龄砰,當(dāng)前展示圖片索引趣斤,進(jìn)場動畫執(zhí)行時間,出場動畫執(zhí)行時間吏奸,transition對應(yīng)唯一key值欢揖,自動生成的包裹元素類型 */
let {imageSrc, currentIndex, enterDelay, leaveDelay, name, component} = props;
return (
<ul className="carousel-image">
<CSSTransitionGroup
component={component}
transitionName={name}
transitionEnterTimeout={enterDelay}
transitionLeaveTimeout={leaveDelay}
className={name}>
<img
src={imageSrc[currentIndex]}
key={imageSrc[currentIndex]}
/>
</CSSTransitionGroup>
</ul>
);
}
CarouselImage.propTypes = propTypes;
export default CarouselImage;
1. ReactCSSTransitionGroup工作原理
當(dāng)組件出現(xiàn)時,會在組件添加transitionName-appear類(transitionName由我們自己設(shè)置值)奋蔚,然后下一時刻會給組件添加transitionName-appear-active類她混;當(dāng)組件進(jìn)場時,給組件添加transitionName-enter類,然后下一時刻會給組件添加transitionName-enter-active類泊碑;當(dāng)組件出場時坤按,會給組件添加transitionName-leave類,然后下一時刻輝給組件添加transitionName-leave-active類馒过,我們則可以在css文件中臭脓,通過設(shè)置transition,設(shè)置我們需要執(zhí)行的動畫腹忽。
一般情況下谢鹊,我們主要使用后兩種,并且留凭,只有當(dāng)組件的出場動畫完全執(zhí)行玩以后,組件才會被移除偎巢。
2. ReactCSSTransitionGroup組件參數(shù)
ReactCSSTransitionGroup其實(shí)就是一個組件蔼夜,它規(guī)定了特定的參數(shù),我們通過設(shè)置這些特定的參數(shù)压昼,將這些參數(shù)反應(yīng)到被其包裹的子組件中求冷。下面,我們就其幾個常見的參數(shù)進(jìn)行講解窍霞。
transitionName: 設(shè)置動態(tài)生成類的自定義前綴匠题,如果我們設(shè)置為carousel-image-item,那么但金,就會相應(yīng)的生成carousel-image-item-enter, carousel-image-item-enter-active等韭山。
component: 字符串,設(shè)置ReactCSSTransitionGroup生成包裹子組件的標(biāo)簽,默認(rèn)時span钱磅,我們可以通過這個參數(shù)自定義梦裂,如div。
transitionEnter: 布爾值盖淡,設(shè)置是否使用出場動畫年柠,默認(rèn)時true。
transitionEnterTimeout: 數(shù)值褪迟,設(shè)置入場動畫的執(zhí)行時間冗恨,需要在css中和這里同時設(shè)置,否則會提示警告味赃。
transitionLeave: 布爾值掀抹,設(shè)置是否使用出場動畫,默認(rèn)時true洁桌。
transitionLeaveTimeout: 數(shù)值渴丸,設(shè)置出場動畫的執(zhí)行時間,需要在css中和這里同時設(shè)置另凌,否則會提示警告谱轨。
4.GSAP
GSAP 是一個老牌的專業(yè)級動畫庫,從古老的 Flash 動畫時代一直興盛至今吠谢,它是一個商業(yè)產(chǎn)品土童,雖然開發(fā)者可以免費(fèi)下載源代碼,但如果要在商業(yè)活動中使用它工坊,請購買相關(guān)的會員献汗。如果你沒有使用果 GSAP,建議閱讀《GSAP王污,專業(yè)的Web動畫庫》罢吃,接下來,我們嘗試將 GSAP 融入到 React 的開發(fā)中昭齐。
將 GSAP 與 React 結(jié)合有一個最簡單的方式:使用 ref
尿招。通過 ref
獲取真實(shí)的 DOM 節(jié)點(diǎn),實(shí)現(xiàn)動畫的方式與傳統(tǒng)的實(shí)現(xiàn)方式一致:
// 導(dǎo)入通過 NPM 安裝的 GSAP
import TweenMax from 'gsap';
// 保存 ref 指向的真實(shí)節(jié)點(diǎn)
let refNode;
class App extends React.Component {
componentDidMount () {
TweenMax.to(refNode, 2, { x: '+=200px', backgroundColor: '#2196f3' });
// TweenMax 可以做什么阱驾?
// 暫停
tween.pause();
// 繼續(xù)播放
tween.resume();
// 反轉(zhuǎn)播放
tween.reverse();
// 跳轉(zhuǎn)到1s進(jìn)度處開始播放
tween.seek(1);
// 重播
tween.restart();
// 動畫變?yōu)槿端? tween.timeScale(3); }
render () {
return ( <div id="ball" ref={c => (refNode = c)}
style={{ width: '100px', height: '100px',
margin: '100px', borderRadius: '50%', backgroundColor: 'red' }}>
</div> ); }