面試官:在React中組件間過渡動畫如何實現(xiàn)?
本文轉(zhuǎn)載自微信公眾號「JS每日一題」,作者灰灰。轉(zhuǎn)載本文請聯(lián)系JS每日一題公眾號。
一、是什么
在日常開發(fā)中,頁面切換時的轉(zhuǎn)場動畫是比較基礎(chǔ)的一個場景
當(dāng)一個組件在顯示與消失過程中存在過渡動畫,可以很好的增加用戶的體驗
在react中實現(xiàn)過渡動畫效果會有很多種選擇,如react-transition-group,react-motion,Animated,以及原生的CSS都能完成切換動畫
二、如何實現(xiàn)
在react中,react-transition-group是一種很好的解決方案,其為元素添加enter,enter-active,exit,exit-active這一系列勾子
可以幫助我們方便的實現(xiàn)組件的入場和離場動畫
其主要提供了三個主要的組件:
- CSSTransition:在前端開發(fā)中,結(jié)合 CSS 來完成過渡動畫效果
- SwitchTransition:兩個組件顯示和隱藏切換時,使用該組件
- TransitionGroup:將多個動畫組件包裹在其中,一般用于列表中元素的動畫
CSSTransition
其實現(xiàn)動畫的原理在于,當(dāng)CSSTransition的in屬性置為true時,CSSTransition首先會給其子組件加上xxx-enter、xxx-enter-active的class執(zhí)行動畫
當(dāng)動畫執(zhí)行結(jié)束后,會移除兩個class,并且添加-enter-done的class
所以可以利用這一點,通過css的transition屬性,讓元素在兩個狀態(tài)之間平滑過渡,從而得到相應(yīng)的動畫效果
當(dāng)in屬性置為false時,CSSTransition會給子組件加上xxx-exit和xxx-exit-active的class,然后開始執(zhí)行動畫,當(dāng)動畫結(jié)束后,移除兩個class,然后添加-enter-done的class
如下例子:
- export default class App2 extends React.PureComponent {
- state = {show: true};
- onToggle = () => this.setState({show: !this.state.show});
- render() {
- const {show} = this.state;
- return (
- <div className={'container'}>
- <div className={'square-wrapper'}>
- <CSSTransition
- in={show}
- timeout={500}
- classNames={'fade'}
- unmountOnExit={true}
- >
- <div className={'square'} />
- </CSSTransition>
- </div>
- <Button onClick={this.onToggle}>toggle</Button>
- </div>
- );
- }
- }
對應(yīng)css樣式如下:
- .fade-enter {
- opacity: 0;
- transform: translateX(100%);
- }
- .fade-enter-active {
- opacity: 1;
- transform: translateX(0);
- transition: all 500ms;
- }
- .fade-exit {
- opacity: 1;
- transform: translateX(0);
- }
- .fade-exit-active {
- opacity: 0;
- transform: translateX(-100%);
- transition: all 500ms;
- }
SwitchTransition
SwitchTransition可以完成兩個組件之間切換的炫酷動畫
比如有一個按鈕需要在on和off之間切換,我們希望看到on先從左側(cè)退出,off再從右側(cè)進入
SwitchTransition中主要有一個屬性mode,對應(yīng)兩個值:
- in-out:表示新組件先進入,舊組件再移除;
- out-in:表示就組件先移除,新組建再進入
SwitchTransition組件里面要有CSSTransition,不能直接包裹你想要切換的組件
里面的CSSTransition組件不再像以前那樣接受in屬性來判斷元素是何種狀態(tài),取而代之的是key屬性
下面給出一個按鈕入場和出場的示例,如下:
- import { SwitchTransition, CSSTransition } from "react-transition-group";
- export default class SwitchAnimation extends PureComponent {
- constructor(props) {
- super(props);
- this.state = {
- isOn: true
- }
- }
- render() {
- const {isOn} = this.state;
- return (
- <SwitchTransition mode="out-in">
- <CSSTransition classNames="btn"
- timeout={500}
- key={isOn ? "on" : "off"}>
- {
- <button onClick={this.btnClick.bind(this)}>
- {isOn ? "on": "off"}
- </button>
- }
- </CSSTransition>
- </SwitchTransition>
- )
- }
- btnClick() {
- this.setState({isOn: !this.state.isOn})
- }
- }
css文件對應(yīng)如下:
- .btn-enter {
- transform: translate(100%, 0);
- opacity: 0;
- }
- .btn-enter-active {
- transform: translate(0, 0);
- opacity: 1;
- transition: all 500ms;
- }
- .btn-exit {
- transform: translate(0, 0);
- opacity: 1;
- }
- .btn-exit-active {
- transform: translate(-100%, 0);
- opacity: 0;
- transition: all 500ms;
- }
TransitionGroup
當(dāng)有一組動畫的時候,就可將這些CSSTransition放入到一個TransitionGroup中來完成動畫
同樣CSSTransition里面沒有in屬性,用到了key屬性
TransitionGroup在感知children發(fā)生變化的時候,先保存移除的節(jié)點,當(dāng)動畫結(jié)束后才真正移除
其處理方式如下:
- 插入的節(jié)點,先渲染dom,然后再做動畫
- 刪除的節(jié)點,先做動畫,然后再刪除dom
如下:
- import React, { PureComponent } from 'react'
- import { CSSTransition, TransitionGroup } from 'react-transition-group';
- export default class GroupAnimation extends PureComponent {
- constructor(props) {
- super(props);
- this.state = {
- friends: []
- }
- }
- render() {
- return (
- <div>
- <TransitionGroup>
- {
- this.state.friends.map((item, index) => {
- return (
- <CSSTransition classNames="friend" timeout={300} key={index}>
- <div>{item}</div>
- </CSSTransition>
- )
- })
- }
- </TransitionGroup>
- <button onClick={e => this.addFriend()}>+friend</button>
- </div>
- )
- }
- addFriend() {
- this.setState({
- friends: [...this.state.friends, "coderwhy"]
- })
- }
- }
對應(yīng)css如下:
- .friend-enter {
- transform: translate(100%, 0);
- opacity: 0;
- }
- .friend-enter-active {
- transform: translate(0, 0);
- opacity: 1;
- transition: all 500ms;
- }
- .friend-exit {
- transform: translate(0, 0);
- opacity: 1;
- }
- .friend-exit-active {
- transform: translate(-100%, 0);
- opacity: 0;
- transition: all 500ms;
- }
參考文獻
- https://segmentfault.com/a/1190000018861018
- https://mp.weixin.qq.com/s/14HneI7SpfrRHKtqgosIiA