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 相关文章推荐
jQuery的12招常用技巧分享
Aug 08 Javascript
jqTransform form表单美化插件使用方法
Jul 05 Javascript
jquery教程ajax请求json数据示例
Jan 13 Javascript
IE8中动态创建script标签onload无效的解决方法
Dec 22 Javascript
javascript感应鼠标图片透明度显示的方法
Feb 24 Javascript
基于javascript实现表格的简单操作
May 21 Javascript
两行代码轻松搞定JavaScript日期验证
Aug 03 Javascript
js 打开新页面在屏幕中间的实现方法
Nov 02 Javascript
Google 爬虫如何抓取 JavaScript 的内容
Apr 07 Javascript
十分钟带你快速了解React16新特性
Nov 10 Javascript
基于Vue 2.0的模块化前端 UI 组件库小结
Dec 21 Javascript
vue-element-admin 菜单标签失效的解决方式
Nov 12 Javascript
关于React Native 无法链接模拟器的问题
React-vscode使用jsx语法的问题及解决方法
vue3使用vue-router的完整步骤记录
一篇文章学会Vue中间件管道
Jun 20 #Vue.js
Ajax 的初步实现(使用vscode+node.js+express框架)
帮你提高开发效率的JavaScript20个技巧
JavaScript实现贪吃蛇游戏
You might like
饭制《星际争霸》Mod:优化游戏机制 增加新单位
2017/07/02 星际争霸
php你的验证码安全码?
2007/01/02 PHP
PHP 采集获取指定网址的内容
2010/01/05 PHP
基于PHP的cURL快速入门教程 (小偷采集程序)
2011/06/02 PHP
php生成QRcode实例
2014/09/22 PHP
php绘图之加载外部图片的方法
2015/01/24 PHP
php简单压缩css样式示例
2016/09/22 PHP
thinkphp Apache配置重启Apache1 restart 出错解决办法
2017/02/15 PHP
php使用PDO事务配合表格读取大量数据插入操作实现方法
2017/02/16 PHP
详解Laravel设置多态关系模型别名的方式
2019/10/17 PHP
THINKPHP5分页数据对象处理过程解析
2020/10/28 PHP
解决extjs在firefox中关闭窗口再打开后iframe中js函数访问不到的问题
2008/11/06 Javascript
Javascript 获取LI里的内容
2008/12/17 Javascript
原生JavaScript实现合并多个数组示例
2014/09/21 Javascript
深入理解JavaScript中Ajax
2016/08/02 Javascript
Javascript下拉刷新的简单实现
2017/02/14 Javascript
ES6新特性七:数组的扩充详解
2017/04/21 Javascript
vue-cli单页应用改成多页应用配置详解
2017/07/14 Javascript
JavaScript展开操作符(Spread operator)详解
2019/07/20 Javascript
Layui 数据表格批量删除和多条件搜索的实例
2019/09/04 Javascript
JavaScript 禁止用户保存图片的实现代码
2020/04/28 Javascript
OpenLayers3实现对地图的基本操作
2020/09/28 Javascript
详解Vue数据驱动原理
2020/11/17 Javascript
python中的yield使用方法
2014/02/11 Python
python自定义解析简单xml格式文件的方法
2015/05/11 Python
python抽象基类用法实例分析
2015/06/04 Python
对numpy中的transpose和swapaxes函数详解
2018/08/02 Python
Python3 pyecharts生成Html文件柱状图及折线图代码实例
2020/09/29 Python
英国在线房屋中介网站:Yopa
2018/01/09 全球购物
澳大利亚著名的纺织品品牌:Canningvale
2020/05/05 全球购物
Unix/Linux开发面试题
2016/08/16 面试题
党的群众教育实践活动实施方案
2014/06/12 职场文书
银行会计主管岗位职责
2014/10/01 职场文书
2015年数学教研工作总结
2015/07/22 职场文书
MySQL和Oracle批量插入SQL的通用写法示例
2021/11/17 MySQL
windows10 家庭版下FTP服务器搭建教程
2022/08/05 Servers