在開發中,我們想要給一個組件的顯示和消失添加某種過渡動畫,可以很好的增加用戶體驗。

當然,我們可以通過原生的CSS來實現這些過渡動畫,但是React社區爲我們提供了react-transition-group用來完成過渡動畫。

一. react-transition-group介紹

React曾爲開發者提供過動畫插件 react-addons-css-transition-group ,後由社區維護,形成了現在的 react-transition-group

這個庫可以幫助我們方便的實現組件的 入場離場 動畫,使用時需要進行額外的安裝:

# npm
npm install react-transition-group --save

# yarn
yarn add react-transition-group

react-transition-group本身非常小,不會爲我們應用程序增加過多的負擔。

react-transition-group主要包含四個組件:

  • Transition

    • 該組件是一個和平臺無關的組件(不一定要結合CSS);

    • 在前端開發中,我們一般是結合CSS來完成樣式,所以比較常用的是CSSTransition;

  • CSSTransition

    • 在前端開發中,通常使用CSSTransition來完成過渡動畫效果

  • SwitchTransition

    • 兩個組件顯示和隱藏切換時,使用該組件

  • TransitionGroup

    • 將多個動畫組件包裹在其中,一般用於列表中元素的動畫;

二. react-transition-group使用

2.1. CSSTransition

CSSTransition是基於Transition組件構建的:

  • CSSTransition執行過程中,有三個狀態:appear、enter、exit;

  • 它們有三種狀態,需要定義對應的CSS樣式:

    • 第一類,開始狀態:對於的類是-appear、-enter、exit;

    • 第二類:執行動畫:對應的類是-appear-active、-enter-active、-exit-active;

    • 第三類:執行結束:對應的類是-appear-done、-enter-done、-exit-done;

CSSTransition常見對應的屬性:

  • in:觸發進入或者退出狀態

    • 如果添加了 unmountOnExit={true} ,那麼該組件會在執行退出動畫結束後被移除掉;
    • 當in爲true時,觸發進入狀態,會添加-enter、-enter-acitve的class開始執行動畫,當動畫執行結束後,會移除兩個class,並且添加-enter-done的class;

    • 當in爲false時,觸發退出狀態,會添加-exit、-exit-active的class開始執行動畫,當動畫執行結束後,會移除兩個class,並且添加-enter-done的class;

  • classNames:動畫class的名稱

    • 決定了在編寫css時,對應的class名稱:比如card-enter、card-enter-active、card-enter-done;

  • timeout:

    • 過渡動畫的時間

  • appear:

    • 是否在初次進入添加動畫(需要和in同時爲true)

  • 其他屬性可以參考官網來學習:

    • https://reactcommunity.org/react-transition-group/transition

CSSTransition對應的鉤子函數:主要爲了檢測動畫的執行過程,來完成一些JavaScript的操作

  • onEnter:在進入動畫之前被觸發;

  • onEntering:在應用進入動畫時被觸發;

  • onEntered:在應用進入動畫結束後被觸發;

import './App.css'

import { CSSTransition } from 'react-transition-group';

import { Card, Avatar, Button } from 'antd';
import { EditOutlined, EllipsisOutlined, SettingOutlined } from '@ant-design/icons';

const { Meta } = Card;

export default class App extends PureComponent {
  constructor(props) {
    super(props);

    this.state = {
      isShowCard: true
    }
  }

  render() {
    return (
      <div>
        <Button type="primary" onClick={e => this.setState({isShowCard: !this.state.isShowCard})}>顯示/隱藏</Button>
        <CSSTransition in={this.state.isShowCard}
                       classNames="card"
                       timeout={1000}
                       unmountOnExit={true}
                       onEnter={el => console.log("進入動畫前")}
                       onEntering={el => console.log("進入動畫")}
                       onEntered={el => console.log("進入動畫後")}
                       onExit={el => console.log("退出動畫前")}
                       onExiting={el => console.log("退出動畫")}
                       onExited={el => console.log("退出動畫後")}
                      >
          <Card
            style={{ width: 300 }}
            cover={
              <img
                alt="example"
                src="https://gw.alipayobjects.com/zos/rmsportal/JiqGstEfoWAOHiTxclqi.png"
              />
            }
            actions={[
              <SettingOutlined key="setting" />,
              <EditOutlined key="edit" />,
              <EllipsisOutlined key="ellipsis" />,
            ]}
          >
            <Meta
              avatar={<Avatar src="https://zos.alipayobjects.com/rmsportal/ODTLcjxAfvqbxHnVXCYX.png" />}
              title="Card title"
              description="This is the description"
            />
          </Card>
        </CSSTransition>
      </div>
    )
  }
}

對應的css樣式如下:

.card-enter, .card-appear {
  opacity: 0;
  transform: scale(.8);
}

.card-enter-active, .card-appear-active {
  opacity: 1;
  transform: scale(1);
  transition: opacity 300ms, transform 300ms;
}

.card-exit {
  opacity: 1;
}

.card-exit-active {
  opacity: 0;
  transform: scale(.8);
  transition: opacity 300ms, transform 300ms;
}

2.2. SwitchTransition

SwitchTransition可以完成兩個組件之間切換的炫酷動畫:

  • 比如我們有一個按鈕需要在on和off之間切換,我們希望看到on先從左側退出,off再從右側進入;

  • 這個動畫在vue中被稱之爲 vue transition modes;

  • react-transition-group中使用SwitchTransition來實現該動畫;

SwitchTransition中主要有一個屬性:mode,有兩個值

  • in-out:表示新組件先進入,舊組件再移除;

  • out-in:表示就組件先移除,新組建再進入;

如何使用SwitchTransition呢?

  • SwitchTransition組件裏面要有CSSTransition或者Transition組件,不能直接包裹你想要切換的組件;

  • SwitchTransition裏面的CSSTransition或Transition組件不再像以前那樣接受in屬性來判斷元素是何種狀態,取而代之的是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代碼:

.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;
}

2.3. TransitionGroup

當我們有一組動畫時,需要將這些CSSTransition放入到一個TransitionGroup中來完成動畫:

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"]
    })
  }
}
相關文章