最近對前端動畫方案作了一些調研,想找到一個簡單且易複製的方案在React組件中使用(也就是用複製粘貼解決80%的問題),最終選擇官方的react-transition-group加animate.csscss
本文並不打算介紹 CSS 動畫可是推薦一些資源,若是你對CSS 動畫比較陌生也先閱讀下面的資源html
animate.css 是一個出色的樣式庫,提供了各類經常使用的CSS 動畫效果,簡易的例子以下基本見名知意這裏就不作額外的解釋了前端
<div class="animated bounce delay-2s">Example</div>
不過大多數的時候咱們必然不須要引入所有樣式,甚至咱們可能只想copy一個動畫效果,在這裏我fork 了一份 animate.css 而後在其構建的過程當中添加了sourcemap方便copyreact
帶sourcemap的DEMO站點打開控制檯開啓複製粘貼之旅css3
CSSTransiotn 會在動畫的生命週期內爲其指定的子元素添加表明其處於指定生命週期的class
假設有以下DEMO
當 CSSTransition
的 in
屬性值切換時true
的時候會依次給chidern
添加 fade-enter, fade-enter-active, fade-enter-done。
當 CSSTransition
的 in
屬性值切換時false
的時候會依次給chidern
添加 fade-exit, fade-exit-active, fade-exit-done。git
其中 -enter-active
緊隨 -enter
以後添加而且跟-enter
同時存在,而-enter-done
在動畫結束時添加而且與-enter-active
和enter
互斥,exit
同理。github
因此當咱們要利用CSSTransition實現動畫效果的時候,只須要定義出對應時間點出現的class
樣式便可,須要注意的倆點web
timeout
決定因此所寫的樣式during
必須跟與其對應(以後咱們會對CSSTransition進行簡單封裝解決這個問題)CSSTransition
決定前綴的參數是classNames
不是className
<CSSTransition in={fadeIn} timeout={2000} unmountOnExit classNames="fade" onEnter={this.onEnter} onEntered={this.onEntered} onExit={this.onExit} onExited={this.onExited} > <div className="demo" >fade-{fadeIn ? 'in' : 'out'}</div> </CSSTransition>
雖然在動畫的執行的生命週期內出現了6個關鍵點可是使用css3 animation
實現動畫效果時咱們只需操做倆個時間點 -enter
和 -exit
就ok了,以後要作的就是在animate.css
copy 對應的代碼less
@keyframes fadeIn { from { opacity: 0; } to { opacity: 1; } } @keyframes fadeOut { from { opacity: 1; } to { opacity: 0; } } .fade { &-enter { animation-name: fadeIn; animation-duration: 2000ms; } &-exit { animation-name: fadeOut; animation-duration: 2000ms; } }
利用React.cloneElement
對爲CSSTransition
默認添加 animated
class,而且經過設置內聯的樣式的方式讓動畫效果結束的時間跟timeout
字段一致前端構建
animated
爲了爲動畫設置一些默認的樣式,好比像下面這樣默認設置動畫時長爲1s
animation-fill-mode
爲 both
.animated { animation-duration: 1s; animation-fill-mode: both; } @media (print), (prefers-reduced-motion) { .animated { animation: unset !important; transition: none !important; } }
import React from 'react' import { CSSTransition } from 'react-transition-group' let count = 0 export default class Animation extends React.Component { static defaultProps = { in: true, timeout: 1000,// 與 .animate 中設置的默認時間對應 unmountOnExit: true, classNames: '', onEnter () {}, onEntered () {}, onExit () {}, onExited () {} } constructor () { super() this.count = count++ } onEnter = () => { console.time(`enter${this.count}`) this.props.onEnter() } onEntered = () => { console.timeEnd(`enter${this.count}`) this.props.onEntered() } onExit = () => { console.time(`exit${this.count}`) this.props.onExit() } onExited = () => { console.timeEnd(`exit${this.count}`) this.props.onExited() } render () { const { in: isIn, timeout, unmountOnExit, classNames, children } = this.props const { props: { className = '', style = {} } } = children return ( <CSSTransition in={isIn} timeout={timeout} unmountOnExit={unmountOnExit} classNames={classNames} onEnter={this.onEnter} onEntered={this.onEntered} onExit={this.onExit} onExited={this.onExited} > {React.cloneElement(children, { className: `animated ${className}`, style: { ...{ '--webkit-animation-duration': `${timeout}ms`, animationDuration: `${timeout}ms` }, ...style } })} </CSSTransition> ) } }