React实现动效弹窗组件


Posted in Javascript onJune 21, 2021

我们在写一些 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 的动效就方便多了。

React实现动效弹窗组件

4. 总结

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

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

Javascript 相关文章推荐
Javascript的数组与字典用法与遍历对象的属性技巧
Nov 07 Javascript
Js动态添加复选框Checkbox的实例方法
Apr 08 Javascript
jquery 定位input元素的几种方法小结
Jul 28 Javascript
使用GruntJS构建Web程序之合并压缩篇
Jun 06 Javascript
js面向对象之常见创建对象的几种方式(工厂模式、构造函数模式、原型模式)
Nov 09 Javascript
JS实现合并两个数组并去除重复项只留一个的方法
Dec 17 Javascript
基于JS实现导航条之调用网页助手小精灵的方法
Jun 17 Javascript
jQuery fadeOut 异步实例代码详解
Aug 18 Javascript
基于jquery实现多选下拉列表
Aug 02 jQuery
基于VUE实现的九宫格抽奖功能
Sep 30 Javascript
解决element ui select下拉框不回显数据问题的解决
Feb 20 Javascript
解决vue net :ERR_CONNECTION_REFUSED报错问题
Aug 13 Javascript
关于React Native 无法链接模拟器的问题
React-vscode使用jsx语法的问题及解决方法
vue3使用vue-router的完整步骤记录
一篇文章学会Vue中间件管道
Jun 20 #Vue.js
Ajax 的初步实现(使用vscode+node.js+express框架)
帮你提高开发效率的JavaScript20个技巧
JavaScript实现贪吃蛇游戏
You might like
php ci框架中加载css和js文件失败的解决方法
2014/03/03 PHP
CI框架(CodeIgniter)公共模型类定义与用法示例
2017/08/10 PHP
tbody元素支持嵌套的注意方法
2007/03/24 Javascript
jQuery 1.3 和 Validation 验证插件1.5.1
2009/07/09 Javascript
javascript 特性检测并非浏览器检测
2010/01/15 Javascript
juqery 学习之四 筛选过滤
2010/11/30 Javascript
jQuery检测滚动条是否到达底部
2015/12/15 Javascript
浅析jquery数组删除指定元素的方法:grep()
2016/05/19 Javascript
Bootstrap表单布局样式代码
2016/05/31 Javascript
JavaScript程序中实现继承特性的方式总结
2016/06/24 Javascript
JavaScript实现图片拖曳效果
2017/09/08 Javascript
vue-cli开发时,关于ajax跨域的解决方法(推荐)
2018/02/03 Javascript
jQuery+koa2实现简单的Ajax请求的示例
2018/03/06 jQuery
JavaScript设计模式之代理模式实例分析
2019/01/16 Javascript
实例详解带参数的 npm script
2019/05/28 Javascript
基于vuex实现购物车功能
2021/01/10 Vue.js
[01:32]DOTA2上海特锦赛现场采访:最想COS的英雄
2016/03/25 DOTA
python持久性管理pickle模块详细介绍
2015/02/18 Python
简介Django中内置的一些中间件
2015/07/24 Python
Python使用装饰器进行django开发实例代码
2018/02/06 Python
Python爬虫之正则表达式的使用教程详解
2018/10/25 Python
对matplotlib改变colorbar位置和方向的方法详解
2018/12/13 Python
python协程之动态添加任务的方法
2019/02/19 Python
Python实现的多进程拷贝文件并显示百分比功能示例
2019/04/09 Python
python的turtle库使用详解
2019/05/10 Python
教你一步步利用python实现贪吃蛇游戏
2019/06/27 Python
基于pytorch padding=SAME的解决方式
2020/02/18 Python
浅谈Pytorch torch.optim优化器个性化的使用
2020/02/20 Python
Pycharm 使用 Pipenv 新建的虚拟环境(图文详解)
2020/04/16 Python
python中count函数知识点浅析
2020/12/17 Python
使用CSS3来代替JS实现交互
2017/08/10 HTML / CSS
Laura Geller官网:美国彩妆品牌
2018/12/29 全球购物
美术专业个人自我评价
2014/01/18 职场文书
学校大课间活动方案
2014/01/30 职场文书
初中军训感想300字
2014/03/05 职场文书
2016继续教育培训学习心得体会
2016/01/19 职场文书