React实现动效弹窗组件

我们在写一些 UI 组件时,若不考虑动效,就很容易实现,主要就是有无的切换(类似于 Vue 中的 v-if 属性)或者可见性的切换(类似于 Vue 中的 v-show 属性)。

1. 没有动效的弹窗

在 React 中,可以这样来实现:

interface ModalProps {
  open: boolean;
  onClose?: () => void;
  children?: any;
}
const Modal = ({open. onClose, children}: ModalProps) => {
  if (!open) {
    return null;
  }
  return createPortal(<div>
    <div classname="modal-content">{children}</div>
    <div classname="modal-close-btn" onclick="{onClose}">x</div>
  </div>, document.body);
};

使用方式:

const App = () => {
  const [open, setOpen] = useState(false);

  return (
    <div classname="app">
      <button onclick="{()" ==""> setOpen(true)}>show modal</button>
      <modal open="{open}" onclose="{()" ==""> setOpen(false)}>
        modal content
      </modal>
    </div>
  );
};

我们在这里就是使用open属性来控制展示还是不展示,但完全没有渐变的效果。

若我们想实现 fade, zoom 等动画效果,还需要对此进行改造。

2. 自己动手实现有动效的弹窗

很多同学在自己实现动效时,经常是展示的时候有动效,关闭的时候没有动效。都是动效的时机没有控制好。这里我们先自己来实现一下动效的流转。

刚开始我实现的时候,动效只有开始状态和结束状态,需要很多的变量和逻辑来控制这个动效。

后来我参考了react-transition-group组件的实现,他是将动效拆分成了几个部分,每个部分分别进行控制。

  1. 展开动效的顺序:enter -> enter-active -> enter-done;
  2. 关闭动效的顺序:exit -> exit-active -> exit-done;

动效过程在enter-activeexit-active的过程中。

我们再通过一个变量 active 来控制是关闭动效是否已执行关闭,参数 open 只控制是执行展开动效还是关闭动效。

当 open 和 active 都为 false 时,才销毁弹窗。

const Modal = ({ open, children, onClose }) => {
  const [active, setActive] = useState(false); // 弹窗的存在周期

  if (!open && !active) {
    return null;
  }

  return ReactDOM.createPortal(
    <div classname="modal">
      <div classname="modal-content">{children}</div>
      <div classname="modal-close-btn" onclick="{onClose}">
        x
      </div>
    </div>,
    document.body,
  );
};

这里我们接着添加动效过程的变化:

const [aniClassName, setAniClassName] = useState(''); // 动效的class

// transition执行完毕的监听函数
const onTransitionEnd = () => {
  // 当open为rue时,则结束状态为'enter-done'
  // 当open未false时,则结束状态为'exit-done'
  setAniClassName(open ? 'enter-done' : 'exit-done');

  // 若open为false,则动画结束时,弹窗的生命周期结束
  if (!open) {
    setActive(false);
  }
};

useEffect(() => {
  if (open) {
    setActive(true);
    setAniClassName('enter');
    // setTimeout用来切换class,让transition动起来
    setTimeout(() => {
      setAniClassName('enter-active');
    });
  } else {
    setAniClassName('exit');
    setTimeout(() => {
      setAniClassName('exit-active');
    });
  }
}, [open]);

Modal 组件完整的代码如下:

const Modal = ({ open, children, onClose }) => {
  const [active, setActive] = useState(false); // 弹窗的存在周期
  const [aniClassName, setAniClassName] = useState(''); // 动效的class
  const onTransitionEnd = () => {
    setAniClassName(open ? 'enter-done' : 'exit-done');
    if (!open) {
      setActive(false);
    }
  };

  useEffect(() => {
    if (open) {
      setActive(true);
      setAniClassName('enter');
      setTimeout(() => {
        setAniClassName('enter-active');
      });
    } else {
      setAniClassName('exit');
      setTimeout(() => {
        setAniClassName('exit-active');
      });
    }
  }, [open]);

  if (!open && !active) {
    return null;
  }

  return ReactDOM.createPortal(
    <div classname="{'modal" '="" +="" aniclassname}="" ontransitionend="{onTransitionEnd}">
      <div classname="modal-content">{children}</div>
      <div classname="modal-close-btn" onclick="{onClose}">
        x
      </div>
    </div>,
    document.body,
  );
};

动效的流转过程已经实现了,样式也要一起写上。比如我们要实现渐隐渐现的 fade 效果:

.enter {
  opacity: 0;
}
.enter-active {
  transition: opacity 200ms ease-in-out;
  opacity: 1;
}
.enter-done {
  opacity: 1;
}
.exit {
  opacity: 1;
}
.exit-active {
  opacity: 0;
  transition: opacity 200ms ease-in-out;
}
.exit-done {
  opacity: 0;
}

如果是要实现放大缩小的 zoom 效果,修改这几个 class 就行。

一个带有动效的弹窗就已经实现了。

使用方式:

const App = () => {
  const [open, setOpen] = useState(false);

  return (
    <div classname="app">
      <button onclick="{()" ==""> setOpen(true)}>show modal</button>
      <modal open="{open}" onclose="{()" ==""> setOpen(false)}>
        modal content
      </modal>
    </div>
  );
};

点击链接自己实现动效的 React 弹窗 demo查看效果。

类似地,还有 Toast 之类的,也可以这样实现。

3. react-transition-group

我们在实现动效的思路上借鉴了 react-transition-group 中的CSSTransition组件。CSSTransition已经帮我封装好了动效展开和关闭的过程,我们在实现弹窗时,可以直接使用该组件。

这里有一个重要的属性:unmountOnExit,表示在动效结束后,卸载该组件。

const Modal = ({ open, onClose }) => {
  // http://reactcommunity.org/react-transition-group/css-transition/
  // in属性为true/false,true为展开动效,false为关闭动效
  return createPortal(
    <csstransition in="{open}" timeout="{200}" unmountonexit="">
      <div classname="modal">
        <div classname="modal-content">{children}</div>
        <div classname="modal-close-btn" onclick="{onClose}">
          x
        </div>
      </div>
    </csstransition>,
    document.body,
  );
};

在使用 CSSTransition 组件后,Modal 的动效就方便多了。

4. 总结

至此已把待动效的 React Modal 组件实现出来了。虽然 React 中没有类似 Vue 官方定义的<transition>标签,不过我们可以自己或者借助第三方组件来实现。

以上就是React实现动效弹窗组件的详细内容,更多关于React弹窗组件的资料请关注我们其它相关文章!

(0)

相关推荐

  • 详解React中传入组件的props改变时更新组件的几种实现方法

    我们使用react的时候常常需要在一个组件传入的props更新时重新渲染该组件,常用的方法是在componentWillReceiveProps中将新的props更新到组件的state中(这种state被成为派生状态(Derived State)),从而实现重新渲染.React 16.3中还引入了一个新的钩子函数getDerivedStateFromProps来专门实现这一需求.但无论是用componentWillReceiveProps还是getDerivedStateFromProps都不是

  • react的滑动图片验证码组件的示例代码

    业务需求,需要在系统登陆的时候,使用"滑动图片验证码",来验证操作的不是机器人. 效果图 使用方式 在一般的页面组件引用即可.onReload这个函数一般是用来请求后台图片的. class App extends Component { state = { url: "" } componentDidMount() { this.setState({ url: getImage() }) } onReload = () => { this.setState({

  • React实现全局组件的Toast轻提示效果

    Toast是常用的轻提示弹框,常用于页面loading和提示语弹窗. 本例基于React实现一个随时可调用且不随页面渲染的全局组件. 需求分析 Toast 不需要同页面一起被渲染,而是根据需要被随时调用. Toast 是一个轻量级的提示组件,它的提示不会打断用户操作,并且会在提示的一段时间后自动关闭. Toast 需要提供几种不同的消息类型以适应不同的使用场景. Toast 的方法必须足够简洁,以避免不必要的代码冗余. 如何使用 首先引入 import Toast from './compone

  • React父子组件间的传值的方法

    父组件向子组件传值: 父组件: import React, { Component } from 'react'; import Child from './chlid'; class parent extends Component{ constructor(props) { super(props); this.state = { txt0:"默认值0", txt1:"默认值1" } } componentDidMount(){ } parToson(){ th

  • react 兄弟组件如何调用对方的方法示例

    最近有一个场景是Child2组件点击让Child1组件里面的state的值发生改变,Child1是一个公用组件,把里面的state值改为props传递,修改内容太多,容易出错,就想找其他的方法来解决兄弟组件调用方法问题,下面看代码: Child1 是第一个子组件 class Child1 extends React.Component { constructor(props) { super(props); this.state = { text:'Child1' }; } onChange=(

  • react组件从搭建脚手架到在npm发布的步骤实现

    最近公司给公司里架设了私有的npm仓库,相应地也需要一个用来发布react组件用的脚手架,在这个过程中又又又又复习了一下webpack,在这里分享下脚手架搭建的过程. 首先,我们预期的脚手架具有如下功能 开发组件时可以实时预览 对组件各种资源进行打包(js/css/图片等) 一键打包发布 1.创建项目 脚手架的名字暂时取react-simple-component-boilerplate. 首先创建一个新目录用于放我们的文件: mkdir react-simple-component-boile

  • React实现动效弹窗组件

    我们在写一些 UI 组件时,若不考虑动效,就很容易实现,主要就是有无的切换(类似于 Vue 中的 v-if 属性)或者可见性的切换(类似于 Vue 中的 v-show 属性). 1. 没有动效的弹窗 在 React 中,可以这样来实现: interface ModalProps { open: boolean; onClose?: () => void; children?: any; } const Modal = ({open. onClose, children}: ModalProps)

  • Flutter使用AnimatedBuilder实现动效复用

    目录 前言 AnimatedBuilder 介绍 Transform 组件介绍 应用 总结 前言 我们之前讲述了动画构建的两种方式,Animation 和 AnimationWidget,这两种构建动画都是将组件和动画一起完成的.有些时候,我们只是想动效复用,而不关心组件构建,这个时候就可以使用 AnimatedBuilder 了. AnimatedBuilder 介绍 根据官方文档说明,AnimatedBuilder 的使用要点如下: An AnimatedBuilder understand

  • 微信小程序vant弹窗组件的实现方式

    作为从事前端开发的你肯定见过不少的弹框组件,你可曾有想过要自己实现一个弹框组件库,又或者想完全定制化的使用各种标准UI框架中的弹框组件呢? 今天这篇文章将会带着你解析这一系列疑问,以vant-weapp组件库为例,从开发标准的弹窗组件使用到高度定制复合自我审美的弹窗,再到完全研究清楚vant-weapp框架弹窗组件部分源码. 一.vant-weapp弹窗组件介绍 vant-weapp组件库是有赞团队开发的 一款灵活简洁且美观的小程序UI组件库 ,此文将以这个组件库的用法为标准,下文提及的弹框组件

  • vue弹窗组件使用方法

    本文实例为大家分享了vue弹窗组件的具体代码,供大家参考,具体内容如下 弹窗是一个项目必备的复用利器,所以封装起来,保证项目ui一致,是很有必要的.学了一段时间vue,想想还是用vue写一下吧.用的很小白,但是会写出来了,说明我也有进步一丢丢了.继续加油-. 代码贴图如下,样式比较丑,不要介意- <!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8">

  • Vue 路由 过渡动效 数据获取方法

    过渡动效 <router-view> 是基本的动态组件,所以我们可以用 <transition> 组件给它添加一些过渡效果: <transition> <router-view></router-view> </transition> 单个路由的过渡 上面的用法会给所有路由设置一样的过渡效果,如果你想让每个路由组件有各自的过渡效果,可以在各路由组件内使用 <transition> 并设置不同的 name. 基于路由的动态过

  • vue工程全局设置ajax的等待动效的方法

    最近在做vue的项目,使用了element-ui作为ui组件库,采用vuex进行状态管理,与后台的请求交互采用axios库实现,原本做的页面,ajax请求个数也只有三个,将等待动画的显示和隐藏通过mutation去控制,但是项目越来越大,请求也越来越多,能否将这个等待动画与ajax的请求相关联呢? 实现等待动效 在element-ui中,提供了两个方法进行调用loading组件,一个是在需要遮罩的div容器中添加v-loading指令,另一种就是以服务形式调用:Loading.service(o

  • Flutter自定义实现神奇动效的卡片切换视图的示例代码

    前言 这一段时间,Flutter的势头是越来越猛了,作为一个Android程序猿,我自然也是想要赶紧尝试一把.在学习到动画的这部分后,为了加深对Flutter动画实现的理解,我决定把之前写的一个卡片切换效果的开源小项目,用Flutter"翻译"一遍. 废话不多说,先来看看效果吧: Android iOS Github地址:https://github.com/BakerJQ/Flutter-InfiniteCards 思路 首先,关于卡片的层叠效果,在原Android项目中,是通过Sc

  • Vue实现路由过渡动效的4种方法

    Vue 路由过渡是对 Vue 程序一种快速简便的增加个性化效果的的方法. 可以让你在程序的不同页面之间增加平滑的动画和过渡.如果使用得当,可以使你的程序显得更加专业,从而增强用户体验. 本文中会先介绍使用 Vue 路由过渡的基础知识,然后在举几个例子,为你一些灵感.下面是其中的一个案例: 在 Vue 程序中添加路由 一般 Vue 路由设置如下所示: <template> <router-view /> </template> 在旧版本的 Vue 路由中,我们可以简单地用

  • 详解Flutter如何绘制曲线,折线图及波浪动效

    目录 正弦曲线绘制 波浪动效 曲线绘制 折线图 其他说明 总结 简介 上一篇用 Flutter 的 Canvas 画点有趣的图形我们介绍了使用 CustomPaint 绘制自定义形状,可以看到有了图形的平面绘制数学计算方法,我们可以画出所需的形状.本篇我们来介绍线条类图形的绘制,并且结合 Animation 实现了常见的波浪动效.通过本篇,你可以了解到: 正弦曲线的绘制 利用两条正弦曲线加上 Animation 实现波浪动效 曲线的一般绘制方法 折线图绘制 下面是最终实现的效果图,接下来我们一项

  • 基于Flutter实现转场动效的示例代码

    目录 前言 CupertinoFullscreenDialogTransition CupertinoPageTransition DecoratedBoxTransition FadeTransition PositionedTransition RotationTransition ScaleTransition SizeTransition SlideTransition 前言 动画经常会用于场景切换,比如滑动,缩放,尺寸变化,为应对这样的场景转换需要,Flutter 提供了 Transi

随机推荐