Skip to content

在react中怎么实现组件间的过渡动画?

Posted on:2024年8月10日 at 17:05

一、是什么

在日常开发中,页面切换时的转场动画是比较基础的一个场景

当一个组件在显示与消失过程中存在过渡动画,可以很好的增加用户的体验

react中实现过渡动画效果会有很多种选择,如react-transition-groupreact-motionAnimated,以及原生的CSS都能完成切换动画

二、如何实现

react中,react-transition-group是一种很好的解决方案,其为元素添加enterenter-activeexitexit-active这一系列勾子

可以帮助我们方便的实现组件的入场和离场动画

其主要提供了三个主要的组件:

CSSTransition

其实现动画的原理在于,当CSSTransitionin属性置为true时,CSSTransition首先会给其子组件加上xxx-enterxxx-enter-activeclass执行动画

当动画执行结束后,会移除两个class,并且添加-enter-doneclass

所以可以利用这一点,通过csstransition属性,让元素在两个状态之间平滑过渡,从而得到相应的动画效果

in属性置为false时,CSSTransition会给子组件加上xxx-exitxxx-exit-activeclass,然后开始执行动画,当动画结束后,移除两个class,然后添加-enter-doneclass

如下例子:

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

对应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可以完成两个组件之间切换的炫酷动画

比如有一个按钮需要在onoff之间切换,我们希望看到on先从左侧退出,off再从右侧进入

SwitchTransition中主要有一个属性mode,对应两个值:

SwitchTransition组件里面要有CSSTransition,不能直接包裹你想要切换的组件

里面的CSSTransition组件不再像以前那样接受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;
}

TransitionGroup

当有一组动画的时候,就可将这些CSSTransition放入到一个TransitionGroup中来完成动画

同样CSSTransition里面没有in属性,用到了key属性

TransitionGroup在感知children发生变化的时候,先保存移除的节点,当动画结束后才真正移除

其处理方式如下:

如下:

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"],
    });
  }
}

对应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://fe.ecool.fun/topic/bc832a32-bf3a-4ee0-b980-89332d68596c