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中hasOwnProperty()的作用
Jun 05 Javascript
深入解析contentWindow, contentDocument
Jul 04 Javascript
使用Node.js为其他程序编写扩展的基本方法
Jun 23 Javascript
浅谈Javascript数组(推荐)
May 17 Javascript
JS中Select下拉列表类(支持输入模糊查询)功能
Jan 17 Javascript
支持移动端原生js轮播图
Feb 16 Javascript
js处理包含中文的字符串实例
Oct 11 Javascript
使用Vue构建可重用的分页组件
Mar 26 Javascript
vue实现类似淘宝商品评价页面星级评价及上传多张图片功能
Oct 29 Javascript
vue中选项卡点击切换且能滑动切换功能的实现代码
Nov 25 Javascript
js定时器出现第一次延迟的原因及解决方法
Jan 04 Javascript
Ajax是什么?Ajax高级用法之Axios技术
Apr 21 Javascript
关于React Native 无法链接模拟器的问题
React-vscode使用jsx语法的问题及解决方法
vue3使用vue-router的完整步骤记录
一篇文章学会Vue中间件管道
Jun 20 #Vue.js
Ajax 的初步实现(使用vscode+node.js+express框架)
帮你提高开发效率的JavaScript20个技巧
JavaScript实现贪吃蛇游戏
You might like
深入eAccelerator与memcached的区别详解
2013/06/06 PHP
PHP实现过滤各种HTML标签
2015/05/17 PHP
文件上传之SWFUpload插件(代码)
2015/07/30 PHP
PHP json_encode() 函数详解及中文乱码问题
2015/11/05 PHP
laravel创建类似ThinPHP中functions.php的全局函数
2016/11/26 PHP
PHP图片水印类的封装
2017/07/06 PHP
详细解读php的命名空间(一)
2018/02/21 PHP
基于JavaScript获取鼠标位置的各种方法
2015/12/16 Javascript
jQuery实现的自定义滚动条实例详解
2016/09/20 Javascript
使用JavaScriptCore实现OC和JS交互详解
2017/03/28 Javascript
JavaScript中this的用法及this在不同应用场景的作用解析
2017/04/13 Javascript
关于Angular2 + node接口调试的解决方案
2017/05/28 Javascript
jQuery开源组件BootstrapValidator使用详解
2017/06/29 jQuery
AngularJS中的作用域实例分析
2018/05/16 Javascript
layui 数据表格 根据值(1=业务,2=机构)显示中文名称示例
2019/10/26 Javascript
解决vue项目F5刷新mounted里的函数不执行问题
2019/11/05 Javascript
js、jquery实现列表模糊搜索功能过程解析
2020/03/27 jQuery
微信小程序转化为uni-app项目的方法示例
2020/05/22 Javascript
[01:30:15]DOTA2-DPC中国联赛 正赛 Ehome vs Aster BO3 第二场 2月2日
2021/03/11 DOTA
Python 检查数组元素是否存在类似PHP isset()方法
2014/10/14 Python
Python中字典映射类型的学习教程
2015/08/20 Python
python图片验证码生成代码
2016/07/02 Python
使用python进行文本预处理和提取特征的实例
2018/06/05 Python
Linux下python与C++使用dlib实现人脸检测
2018/06/29 Python
django删除表重建的实现方法
2019/08/28 Python
Django继承自带user表并重写的例子
2019/11/18 Python
python 实现二维字典的键值合并等函数
2019/12/06 Python
Tensorflow限制CPU个数实例
2020/02/06 Python
CSS3的resize属性使用初探
2015/09/27 HTML / CSS
新闻学专业个人求职信写作
2014/02/04 职场文书
模具设计与制造专业推荐信
2014/02/16 职场文书
纪检监察建议书
2014/05/19 职场文书
大学国际贸易专业自荐信
2014/06/05 职场文书
党的群众路线教育实践活动总结
2014/10/30 职场文书
Python实现单例模式的5种方法
2021/06/15 Python
php png失真的原因及解决办法
2021/10/24 PHP