vue3 可拖动的左右面板分割组件实现

目录
  • 分解组件
    • 左侧面板
    • 右侧面板
  • 入参分解
    • props
    • slots
  • 具体实现
    • 如何拖动呢?
    • 事件监听
    • 宽度处理
    • 优化
    • bug
  • git地址

    最近在使用vue的时候,遇到一个需求,实现左右div可通过中间部分拖拽调整宽度,本文就整理一下,分享给大家,具体如下:

    效果图

     

    分解组件

    整体使用flex布局

    左侧面板

    • 面板的具体内容通过slot具名插槽传入。
    • title通过prop传入
    • 可拖动,为了保证内容样式不会被拖动所破坏,对面板的宽度设定最大值/最小值

    右侧面板

    • 右侧面板宽度随着左侧面板的宽度变化而变化,此处需注意,内容的宽度使用flex-auto自动适应。
    • 需要做移动端的自适应。
    • 自适应使用tailwind的媒体查询

     

    入参分解

    props

    • @param {Number} maxWidth 最大宽度
    • @param {Number} minWidth 最小宽度
    • @param {String} leftTitle 左标题
    • @param {String} rightTitle 右标题?
    • @param {Boolean} sotoreage 是否存储与localstorege

    slots

    • left-content {Element} 左侧内容
    • right-content {Element} 右侧内容

     

    具体实现

    如何拖动呢?

    在左侧面板与右侧面板之间添加一个隐藏的盒子,我将这个盒子隐藏在box-shadow之中。具体事件放在这个div中实现

    <div id="line" class="w-2 cursor-move hidden md4:block"onMousedown={hnadleMouseDown}>
    </div>

    事件监听

      const hnadleMouseDown = (evt: MouseEvent) => {
        /* 获取起始点位,并存储 */
        let { pageX, pageY } = evt;
        basePosition.pageX = pageX;
        basePosition.pageY = pageY;
        /* 监听鼠标的移动事件 */
        document.addEventListener("mousemove", handleMouseMove);
        document.addEventListener("mouseup", handleMouseUp);
      };
      const handleMouseMove = evt => {
        /* 阻止浏览器默认事件,防止触发浏览器的手势功能 */
        evt.preventDefault();
        /* 设置定时器,防止dom多次回流 */
        clearTimeout(timer.value);
        timer.value = setTimeout(() => {
          let { pageX } = evt;
          const baseDiv = document.querySelector(".right-border-shadow");
          /* 处理宽度,是否处于最大值/最小值之间 */
          let baseWidth: Number | undefined =
            Number(baseDiv?.clientWidth) + (pageX - basePosition.pageX);
          baseWidth =
            baseWidth > Number(props?.maxWidth) ? props.maxWidth : baseWidth;
          baseWidth =
            Number(baseWidth) < Number(props?.minWidth)
              ? props.minWidth
              : baseWidth;
          baseDiv?.setAttribute("style", `width:${baseWidth}px`);
          /* emit宽度改变的事件 */
          ctx.emit("drugend");
          /* 存储到store */
          setStore(baseWidth);
        }, 50);
      };
      const handleMouseUp = evt => {
        /* 结束拖动之后,取消事件监听,并emit出最终宽度 */
        const width = document.querySelector(".right-border-shadow")?.clientWidth;
        document.removeEventListener("mousemove", handleMouseMove);
        document.removeEventListener("mouseup", handleMouseUp);
        ctx.emit("drugend", width);
      };
    

    宽度处理

    style={`width:${
              store.get("split-width")
                ? store.get("split-width")
                : props.minWidth
                ? props.minWidth
                : 384
            }px`}
    

    优化

    手动改变浏览器视窗宽度

    nextTick(() => {
          ctx.emit("load", ctx);
          MutationObserver = window.MutationObserver;
          if (MutationObserver) {
            /* 监听浏览器的窗口变化,在部分情况下需要这个api */
            mo = new MutationObserver(function() {
              const __wm = document.querySelector("#rezie-id");
              // 只在__wm元素变动才重新调用 __canvasWM
              if (!__wm) {
                // 避免一直触发
                mo.disconnect();
                mo = null;
                ctx.emit("resize");
              }
            });
            mo.observe(document.querySelector("#rezie-id"), {
              attributes: true,
              subtree: true,
              childList: true,
            });
          }
        });
    

    未生效,求指点

    bug

    父组件的onMounted钩子中获取子元素的slot元素节点报错,为null。目前的解决办法是在子组件的onMounted钩子中抛出一个load事件,父组件使用onLoad去处理接下来的逻辑。

     

    git地址

    仓库地址
    预览地址

    关于vue3 可拖动的左右面板分割组件实现的文章就介绍至此,更多相关vue3 可拖动左右分割面板内容请搜索编程宝库以前的文章,希望以后支持编程宝库

     1.需求后台给了一个这样的数据让咱前端去转换为树形结构(没有重复数据)。不多说,先来看看给了一个怎样的数组数据,转换为怎样的树形结构。服务器传过来的数组const arr = [ ...