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 IFrame 强制刷新代码
Jul 23 Javascript
Extjs实现进度条的两种便捷方式
Sep 26 Javascript
jquery实现页面常用的返回顶部效果
Mar 04 Javascript
javascript Promise简单学习使用方法小结
May 17 Javascript
javascript将中国数字格式转换成欧式数字格式的简单实例
Aug 02 Javascript
基于jQuery ligerUI实现分页样式
Sep 18 Javascript
Jquery Easyui日历组件Calender使用详解(23)
Dec 18 Javascript
JavaScript类的继承方法小结【组合继承分析】
Jul 11 Javascript
微信小程序使用swiper组件实现层叠轮播图
Nov 04 Javascript
原生JS检测CSS3动画是否结束的方法详解
Jan 27 Javascript
JS中getElementsByClassName与classList兼容性问题解决方案分析
Aug 07 Javascript
深入理解redux之compose的具体应用
Jan 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
咖啡豆要不要放冰箱的原因
2021/03/04 冲泡冲煮
session 的生命周期是多长
2006/10/09 PHP
PHP 文件类型判断代码
2009/03/13 PHP
jQuery+php实现ajax文件即时上传的详解
2013/06/17 PHP
PHP使用range协议实现输出文件断点续传代码实例
2014/07/04 PHP
Yii控制器中filter过滤器用法分析
2016/07/15 PHP
thinkphp框架实现路由重定义简化url访问地址的方法分析
2020/04/04 PHP
学习YUI.Ext 第七天--关于View&amp;JSONView
2007/03/10 Javascript
JavaScript中的View-Model使用介绍
2011/08/11 Javascript
JavaScript 创建运动框架的实现代码
2013/05/08 Javascript
JQuery设置获取下拉菜单某个选项的值(比较全)
2014/08/05 Javascript
javascript操作数组详解
2014/12/17 Javascript
jQuery中innerWidth()方法用法实例
2015/01/19 Javascript
javascript转换日期字符串为Date日期对象的方法
2015/02/13 Javascript
AngularJS 表达式详解及实例代码
2016/09/14 Javascript
微信小程序 window_x64环境搭建
2016/09/30 Javascript
通过原生JS实现为元素添加事件的方法
2016/11/23 Javascript
浅析jQuery操作select控件的取值和设值
2016/12/07 Javascript
最常见和最有用的字符串相关的方法详解
2017/02/06 Javascript
jquery制作的移动端购物车效果完整示例
2020/02/24 jQuery
jquery实现垂直手风琴菜单
2020/03/04 jQuery
Python素数检测实例分析
2015/06/15 Python
Python 常用string函数详解
2016/05/30 Python
python对于requests的封装方法详解
2019/01/03 Python
Ubuntu下Anaconda和Pycharm配置方法详解
2019/06/14 Python
解决python文件双击运行秒退的问题
2019/06/24 Python
Python 实现数据结构-堆栈和队列的操作方法
2019/07/17 Python
利用Python实现Json序列化库的方法步骤
2020/09/09 Python
西班牙品牌鞋子、服装和配饰在线商店:Esdemarca
2021/02/17 全球购物
秋季运动会加油稿200字
2014/01/11 职场文书
网络技术专业推荐信
2014/02/20 职场文书
2014年百日安全生产活动总结
2014/05/04 职场文书
反四风对照检查材料思想汇报
2014/09/16 职场文书
英文版辞职信
2015/02/28 职场文书
2015选调生工作总结
2015/07/24 职场文书
Apache Hudi数据布局黑科技降低一半查询时间
2022/03/31 Servers