自学内容网 自学内容网

前端vue左侧树的一整套功能实现(一):vue2+vite封装v-resize指令,实现左侧树拖拽宽度和折叠展开

实现v-resize指令,具体以下功能:

  1. 指令接收宽度最大最小值,接收一个id用于localStorage存储拖拽宽度,接收padding
  2. 拖拽时产生虚线拖拽,松开鼠标再进行元素宽度调整
  3. 折叠展开图标使用本地图片

封装一个vite下使用本地图片的函数方法

用于拖拽指令中设置折叠展开图标

/** vite使用动态图片的方式 */
export function requireImg(name) {
  return new URL(`/src/assets/imgs/${name}`, import.meta.url).href
}

v-resize指令具体代码

// 注意,需要去除绑定元素的overflow:auto,指令会添加一个具有overflow:auto的元素
let resize;
let stopResize;
const vResize = {
  bind(el, binding) {
    // 从绑定值中获取最小宽度和最大宽度
    const {
      minWidth = 150,
      maxWidth = 400,
      id = '',
      padding = 10,
    } = binding.value || {};

    // 拖拽元素内部插入一个包裹元素,方便控制所有子元素隐藏显示
    const wrapper = document.createElement('div');
    wrapper.style.cssText = `width: 100%; height: 100%; overflow: auto; padding:${padding}px`;
    while (el.firstChild) {
      // appendChild 方法用于将一个节点添加到另一个节点的子节点列表的末尾。
      // 如果要添加的节点已经存在于文档树中,appendChild 方法会将该节点从其当前位置移动到新的位置,而不是复制该节点。
      // 这样就可以将 el 的所有子元素通过循环全部插入至 wrapper 中
      wrapper.appendChild(el.firstChild);
    }
    el.appendChild(wrapper);

    // 创建拖拽元素
    const resizer = document.createElement('div');
    resizer.style.cssText =
      'width: 10px; height: 100%; position: absolute; right: -10px; top: 0px; cursor: ew-resize; user-select: none; display: flex; justify-content: center; align-items: center; z-index: 999;';
    el.style.position = 'relative';
    el.style.padding = '0px';
    el.appendChild(resizer);
    el.style.transition = 'width 0.3s ease';

    // 缓存宽度
    const savedWidth = localStorage.getItem('WIDTH' + id);
    if (savedWidth) {
      el.style.width = savedWidth;
      if (el.style.width === '0px') {
        wrapper.style.display = 'none';
      }
    }

    // 创建切换按钮
    const img = document.createElement('img');
    img.src =
      el.style.width === '0px'
        ? requireImg('tree/7.png')
        : requireImg('tree/6.png');
    img.style.cssText = 'cursor:pointer;height:40px;';
    resizer.appendChild(img);

    // 切换显示/隐藏逻辑
    img.addEventListener('mousedown', (e) => {
      e.stopPropagation();
      toggleContainer(el, wrapper, img);
    });

    // 拖拽虚线
    const line = document.createElement('div');
    line.style.cssText =
      'position: absolute; height: 100%; width: 2px; right: 0; top: 0; z-index: 9999; border-right: 0px dashed #409EFF; pointer-events: none;';
    el.appendChild(line);

    // 拖拽事件
    resizer.addEventListener('mousedown', () => {
      document.addEventListener('mousemove', resize);
      document.addEventListener('mouseup', stopResize);
    });

    let newWidth;

    resize = function (e) {
      line.style.borderRight = '2px dashed #409EFF';
      // 使用传入的最小宽度和最大宽度
      const width = e.pageX - el.getBoundingClientRect().left;
      if (width <= 0) {
        newWidth = 0;
      } else {
        newWidth = Math.max(minWidth, Math.min(maxWidth, width));
      }
      line.style.right = `${el.getBoundingClientRect().right - e.pageX}px`;
    };

    stopResize = function () {
      line.style.borderRight = '0px dashed #409EFF';
      document.removeEventListener('mousemove', resize);
      document.removeEventListener('mouseup', stopResize);
      if (newWidth) {
        el.style.width = `${newWidth}px`;
        setTimeout(() => {
          wrapper.style.display = 'block';
        }, 200);
      } else {
        el.style.width = `0px`;
        wrapper.style.display = 'none';
      }
      img.src =
        newWidth === 0 ? requireImg('tree/7.png') : requireImg('tree/6.png');
      localStorage.setItem('WIDTH' + id, el.style.width);
    };

    function toggleContainer(el, wrapper, img) {
      if (el.style.width === '0px') {
        el.style.width = `${minWidth}px`;
        img.src = requireImg('tree/6.png');
        setTimeout(() => {
          wrapper.style.display = 'block';
        }, 200);
      } else {
        wrapper.style.display = 'none';
        el.style.width = '0px';
        img.src = requireImg('tree/7.png');
      }
      localStorage.setItem('WIDTH' + id, el.style.width);
    }
  },
  unbind() {
    // 清除所有事件监听器,防止内存泄漏
    document.removeEventListener('mousemove', resize);
    document.removeEventListener('mouseup', stopResize);
  },
};

Vue.directive('resize', vResize);

使用指令

 <div class="page">
    <div
      class="left"
      v-resize="{
        id: 'left_tree',
        minWidth: '473',
        maxWidth: '773',
      }"
      v-loading="treeloading">
      ...
    </div>
    <div class="right">
      ...
    </div>
  </div>
.page{
   display:flex;
   .left_tree{
     width:550px;
   }
   .right{
     flex:1;
   }
}

原文地址:https://blog.csdn.net/qq_42611074/article/details/142355224

免责声明:本站文章内容转载自网络资源,如本站内容侵犯了原著者的合法权益,可联系本站删除。更多内容请关注自学内容网(zxcms.com)!