zoukankan      html  css  js  c++  java
  • 实现一个带有动效的 React 弹窗组件

    我们在写一些 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组件的实现,他是将动效拆分成了几个部分,每个部分分别进行控制。

    • 展开动效的顺序:enter -> enter-active -> enter-done;
    • 关闭动效的顺序: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 的动效就方便多了。

    modal的动效

    4. 总结

    帮我点个在看吧

    至此已把待动效的 React Modal 组件实现出来了。虽然 React 中没有类似 Vue 官方定义的<transition>标签,不过我们可以自己或者借助第三方组件来实现。

    欢迎关注我的公众号:“前端小茶馆”,

    前端小茶馆

  • 相关阅读:
    Ubuntu 16 安装redis客户端
    crontab 参数详解
    PHP模拟登录发送闪存
    Nginx配置端口访问的网站
    Linux 增加对外开放的端口
    Linux 实用指令之查看端口开启情况
    无敌的极路由
    不同的域名可以指向同一个项目
    MISCONF Redis is configured to save RDB snapshots, but is currently not able to persist on disk. Commands that may modify the data set are disabled. Please check Redis logs for details about the error
    Redis 创建多个端口
  • 原文地址:https://www.cnblogs.com/xumengxuan/p/14912990.html
Copyright © 2011-2022 走看看