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 ajax abort()的使用方法
Oct 28 Javascript
Javascript 自适应高度的Tab选项卡
Apr 05 Javascript
js 判断脚本加载完毕的代码
Jul 13 Javascript
jQuery调用RESTful WCF示例代码(GET方法/POST方法)
Jan 26 Javascript
JS实现转动随机数抽奖特效代码
Apr 16 Javascript
jQuery实现简洁的导航菜单效果
Nov 23 Javascript
Angular1.x复杂指令实例详解
Mar 01 Javascript
Vue上传组件vue Simple Uploader的用法示例
Aug 25 Javascript
node使用promise替代回调函数
May 07 Javascript
详解如何在Vue项目中导出Excel
Apr 19 Javascript
javascript实现超好看的3D烟花特效
Jan 01 Javascript
vue项目前端微信JSAPI与外部H5支付相关实现过程及常见问题
Apr 14 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中使用ExcelFileParser处理excel获得数据(可作批量导入到数据库使用)
2010/08/21 PHP
Laravel ORM 数据model操作教程
2019/10/21 PHP
(function($){...})(jQuery)的意思
2010/07/22 Javascript
jQuery获取Select选择的Text和Value(详细汇总)
2013/01/25 Javascript
Jquery的hover方法让鼠标经过li时背景变色
2013/09/06 Javascript
jQuery针对各类元素操作基础教程
2014/08/29 Javascript
node.js中的buffer.write方法使用说明
2014/12/10 Javascript
JavaScript实现彩虹文字效果的方法
2015/04/16 Javascript
jQuery蓝色风格滑动导航栏代码分享
2015/08/19 Javascript
jQuery绑定事件监听bind和移除事件监听unbind用法实例详解
2016/01/19 Javascript
基于javascript制作微博发布栏效果
2016/04/04 Javascript
jQuery插件学习教程之SlidesJs轮播+Validation验证
2016/07/12 Javascript
微信小程序 地图map详解及简单实例
2017/01/10 Javascript
小程序rich-text组件如何改变内部img图片样式的方法
2019/05/22 Javascript
详解nuxt 微信公众号支付遇到的问题与解决
2019/08/26 Javascript
Vue v-model组件封装(类似弹窗组件)
2020/01/08 Javascript
JavaScript代理模式原理与用法实例详解
2020/03/10 Javascript
如何基于jQuery实现五角星评分
2020/09/02 jQuery
[42:20]Secret vs Liquid 2019国际邀请赛小组赛 BO2 第二场 8.15
2019/08/17 DOTA
python paramiko实现ssh远程访问的方法
2013/12/03 Python
mysql 之通过配置文件链接数据库
2017/08/12 Python
Python实现合并同一个文件夹下所有PDF文件的方法示例
2018/04/28 Python
Django ManyToManyField 跨越中间表查询的方法
2018/12/18 Python
Python中函数参数匹配模型详解
2019/06/09 Python
新手学python应该下哪个版本
2020/06/11 Python
Python Tricks 使用 pywinrm 远程控制 Windows 主机的方法
2020/07/21 Python
详解如何在pyqt中通过OpenCV实现对窗口的透视变换
2020/09/20 Python
CSS3 @media的基本用法总结
2019/09/10 HTML / CSS
Java如何读取CLOB字段
2013/10/10 面试题
环境保护建议书
2014/08/26 职场文书
社区工作者个人总结
2015/02/28 职场文书
2015年感恩母亲节活动方案
2015/05/04 职场文书
班主任寄语2016
2015/12/04 职场文书
MySQL索引篇之千万级数据实战测试
2021/04/05 MySQL
Pytorch中的数据集划分&正则化方法
2021/05/27 Python
Spring Boot 实现 WebSocket
2022/04/30 Java/Android