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 19 Javascript
关于JS管理作用域的问题
Apr 10 Javascript
Jquery通过Ajax访问XML数据的小例子
Nov 18 Javascript
jquery插件开发之实现jquery手风琴功能分享
Mar 10 Javascript
js实现人民币大写金额形式转换
Apr 27 Javascript
JS数组返回去重后数据的方法解析
Jan 03 Javascript
微信小程序实现多宫格抽奖活动
Apr 15 Javascript
详解Vue源码之数据的代理访问
Dec 11 Javascript
使用vue-cli3新建一个项目并写好基本配置(推荐)
Apr 24 Javascript
vue增加强缓存和版本号的实现方法
May 01 Javascript
过滤器vue.filters的使用方法实现
Sep 18 Javascript
小程序开发之模态框组件封装
Apr 23 Javascript
关于React Native 无法链接模拟器的问题
React-vscode使用jsx语法的问题及解决方法
vue3使用vue-router的完整步骤记录
一篇文章学会Vue中间件管道
Jun 20 #Vue.js
Ajax 的初步实现(使用vscode+node.js+express框架)
帮你提高开发效率的JavaScript20个技巧
JavaScript实现贪吃蛇游戏
You might like
阿拉伯的咖啡与水烟
2021/03/03 咖啡文化
PHP与SQL注入攻击防范小技巧
2011/09/16 PHP
巧用php中的array_filter()函数去掉多维空值的代码分享
2012/09/07 PHP
php unset全局变量运用问题的深入解析
2013/06/17 PHP
PHP中mb_convert_encoding与iconv函数的深入解析
2013/06/21 PHP
C#使用PHP服务端的Web Service通信实例
2014/04/08 PHP
Mac下php 5升级到php 7的步骤详解
2017/04/26 PHP
PHP join()函数用法与实例讲解
2019/03/11 PHP
获取页面高度,窗口高度,滚动条高度等参数值getPageSize,getPageScroll
2006/09/22 Javascript
另类调用flash无须激活的方法
2006/12/27 Javascript
JS类库Bindows1.3中的内存释放方式分析
2007/03/08 Javascript
JavaScript入门学习书籍推荐
2008/06/12 Javascript
Javascript 对象的解释
2008/11/24 Javascript
基于jQuery实现点击同时更改两个iframe的网址
2010/07/01 Javascript
jQuery图片拖动组件Dropzone用法示例
2017/01/17 Javascript
JS实现的简单表单验证功能完整实例
2017/10/14 Javascript
vue+iview/elementUi实现城市多选
2019/03/28 Javascript
vue学习笔记之作用域插槽实例分析
2020/02/01 Javascript
jquery传参及获取方式(两种方式)
2020/02/13 jQuery
vue+element-ui JYAdmin后台管理系统模板解析
2020/07/28 Javascript
Python基于time模块求程序运行时间的方法
2017/09/18 Python
浅谈Python脚本开头及导包注释自动添加方法
2018/10/27 Python
python 读取鼠标点击坐标的实例
2018/12/29 Python
详解Python中is和==的区别
2019/03/21 Python
python自制包并用pip免提交到pypi仅安装到本机【推荐】
2019/06/03 Python
简单了解python调用其他脚本方法实例
2020/03/26 Python
scrapy利用selenium爬取豆瓣阅读的全步骤
2020/09/20 Python
生产车间班组长岗位职责
2014/01/06 职场文书
经典导游欢迎词大全
2014/01/16 职场文书
毕业自我评价
2014/02/05 职场文书
餐厅经理岗位职责和岗位目标
2014/02/13 职场文书
淘宝客服专员岗位职责
2014/04/11 职场文书
2015选调生工作总结
2015/07/24 职场文书
2016入党心得体会范文
2016/01/06 职场文书
用Python爬虫破解滑动验证码的案例解析
2021/05/06 Python
写好Python代码的几条重要技巧
2021/05/21 Python