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的试卷自动排版系统实现代码
Jan 06 Javascript
分页栏的web标准实现
Nov 01 Javascript
node.js集成百度UE编辑器
Feb 05 Javascript
基于Bootstrap使用jQuery实现简单可编辑表格
May 04 Javascript
js数组去重的N种方法(小结)
Jun 07 Javascript
在vue中使用vuex,修改state的值示例
Nov 08 Javascript
js布局实现单选按钮控件
Jan 17 Javascript
使用webpack搭建pixi.js开发环境
Feb 12 Javascript
jquery实现垂直手风琴菜单
Mar 04 jQuery
JS apply用法总结和使用场景实例分析
Mar 14 Javascript
jenkins自动构建发布vue项目的方法步骤
Jan 04 Vue.js
利用uni-app生成微信小程序的踩坑记录
Apr 05 Javascript
关于React Native 无法链接模拟器的问题
React-vscode使用jsx语法的问题及解决方法
vue3使用vue-router的完整步骤记录
一篇文章学会Vue中间件管道
Jun 20 #Vue.js
Ajax 的初步实现(使用vscode+node.js+express框架)
帮你提高开发效率的JavaScript20个技巧
JavaScript实现贪吃蛇游戏
You might like
星际原理概述
2020/03/04 星际争霸
ThinkPHP中实例Model方法的区别说明
2010/08/21 PHP
兼容firefox,chrome的网页灰度效果
2011/08/08 PHP
PHP中new static()与new self()的区别异同分析
2014/08/22 PHP
Laravel中使用阿里云OSS Composer包分享
2015/02/10 PHP
php同时使用session和cookie来保存用户登录信息的实现代码
2016/05/13 PHP
注意!PHP 7中不要做的10件事
2016/09/18 PHP
javascript代码加载优化方法
2011/01/30 Javascript
在JavaScript里防止事件函数高频触发和高频调用的方法
2014/09/06 Javascript
javascript表格隔行变色加鼠标移入移出及点击效果的方法
2015/04/10 Javascript
JS实现字符串转日期并比较大小实例分析
2015/12/09 Javascript
jquery实现列表上下移动功能
2016/02/25 Javascript
canvas 实现中国象棋
2017/02/17 Javascript
微信小程序 template模板详解及实例
2017/02/21 Javascript
详解angularJs指令的3种绑定策略
2017/04/13 Javascript
浅析Vue自定义组件的v-model
2017/11/26 Javascript
vue-router源码之history类的浅析
2019/05/21 Javascript
Vue组件间的通信pubsub-js实现步骤解析
2020/03/11 Javascript
在Python中操作字符串之startswith()方法的使用
2015/05/20 Python
详解Django缓存处理中Vary头部的使用
2015/07/24 Python
批处理与python代码混合编程的方法
2016/05/19 Python
Python整数对象实现原理详解
2019/07/01 Python
Python新手如何进行闭包时绑定变量操作
2020/05/29 Python
python中导入 train_test_split提示错误的解决
2020/06/19 Python
使用OpenCV实现道路车辆计数的使用方法
2020/07/15 Python
Python eval函数原理及用法解析
2020/11/14 Python
CSS3弹性盒模型开发笔记(二)
2016/04/26 HTML / CSS
印度低票价航空公司:GoAir
2017/10/11 全球购物
美国大码时尚女装购物网站:ELOQUII
2017/12/28 全球购物
大学校园毕业自我鉴定
2014/01/15 职场文书
计划生育证明格式范本
2014/09/12 职场文书
向国旗敬礼活动总结
2014/09/27 职场文书
2014大学班主任工作总结
2014/11/08 职场文书
党员学习型组织心得体会
2019/06/21 职场文书
大学迎新生的欢迎词
2019/06/25 职场文书
导游词之南迦巴瓦峰
2019/11/19 职场文书