若依前端vue 项目 里面dragWidth.js 文件详解

0 阅读4分钟

以下是基于该指令核心逻辑扩展的3个实用案例,覆盖不同场景,附带完整代码和使用说明:

完整笔记直接主页联系,或者B站有完整视频

image.png

案例1:左右分栏联动调整(适用于编辑器、后台布局)

场景:在左右分栏布局中(如左侧菜单+右侧内容、编辑区+预览区),拖拽中间手柄同时调整两侧宽度,保持总宽度不变。

image.png

指令代码(directive/columnResize.js

image.png


export default {
  bind(el) {
    // 查找左右分栏元素(需在模板中定义对应类名)
    const leftDom = el.querySelector('.left-column');
    const rightDom = el.querySelector('.right-column');
    if (!leftDom || !rightDom) {
      console.error('未找到左右分栏元素,请添加 .left-column 和 .right-column 类名');
      return;
    }
​
    // 获取父容器总宽度(左右分栏总宽 = 父容器宽)
    const parentWidth = el.offsetWidth;
​
    // 创建中间拖拽手柄
    const lineEl = document.createElement('div');
    lineEl.style = `
      width: 5px; 
      height: 100%; 
      background: #e0e0e0; 
      position: absolute; 
      left: ${leftDom.offsetWidth}px; /* 初始位置在左栏右侧 */
      top: 0; 
      z-index: 10; 
      cursor: col-resize; /* 水平调整光标 */
      transition: background 0.2s;
    `;
    // 鼠标悬停时高亮手柄
    lineEl.onmouseover = () => lineEl.style.background = '#ccc';
    lineEl.onmouseout = () => lineEl.style.background = '#e0e0e0';
​
    // 绑定鼠标按下事件
    lineEl.addEventListener('mousedown', (e) => {
      e.preventDefault();
      // 记录鼠标按下时的X坐标和左栏宽度
      const startX = e.clientX;
      const startLeftWidth = leftDom.offsetWidth;
​
      // 鼠标移动时调整宽度
      const handleMouseMove = (e) => {
        e.preventDefault();
        // 计算左栏宽度变化量(限制最小宽度为200px,最大为父容器的80%)
        const deltaX = e.clientX - startX;
        const newLeftWidth = Math.max(200, Math.min(parentWidth * 0.8, startLeftWidth + deltaX));
        const newRightWidth = parentWidth - newLeftWidth;
​
        // 更新左右分栏宽度和手柄位置
        leftDom.style.width = `${newLeftWidth}px`;
        rightDom.style.width = `${newRightWidth}px`;
        lineEl.style.left = `${newLeftWidth}px`;
      };
​
      // 鼠标松开时移除事件
      const handleMouseUp = () => {
        document.removeEventListener('mousemove', handleMouseMove);
        document.removeEventListener('mouseup', handleMouseUp);
      };
​
      // 绑定全局事件(确保拖拽不中断)
      document.addEventListener('mousemove', handleMouseMove);
      document.addEventListener('mouseup', handleMouseUp);
    }, false);
​
    // 将手柄添加到父容器
    el.appendChild(lineEl);
  }
};

使用示例(ColumnDemo.vue


<template>
  <!-- 父容器:相对定位,包含左右分栏 -->
  <div class="column-container" v-columnResize>
    <!-- 左分栏 -->
    <div class="left-column">
      <h3>左侧菜单</h3>
      <p>拖拽中间手柄可调整宽度</p>
    </div>
    <!-- 右分栏 -->
    <div class="right-column">
      <h3>右侧内容区</h3>
      <p>总宽度固定,左侧变宽时右侧自动变窄</p>
    </div>
  </div>
</template><style scoped>
.column-container {
  position: relative;
  width: 1000px;
  height: 500px;
  margin: 20px auto;
  border: 1px solid #ccc;
}
.left-column {
  float: left;
  width: 300px; /* 初始宽度 */
  height: 100%;
  background: #f5f5f5;
  box-sizing: border-box;
  padding: 20px;
}
.right-column {
  float: left;
  width: 700px; /* 初始宽度 = 总宽 - 左栏宽 */
  height: 100%;
  background: #fff;
  box-sizing: border-box;
  padding: 20px;
}
</style><script>
import columnResize from '@/directive/columnResize.js';
export default {
  directives: {
    columnResize // 局部注册指令
  }
};
</script>

效果:拖拽中间灰色手柄,左侧宽度增加时右侧自动减少,且两侧宽度不会小于200px或超过总宽的80%。

案例2:表格容器高度调整(解决表格内容过多问题)

场景:表格数据过多时,用户可拖拽表格底部手柄调整容器高度,避免频繁滚动。

指令代码(directive/tableHeightResize.js


export default {
  bind(el) {
    // 查找表格容器(需在模板中定义 .table-container 类名)
    const tableContainer = el.querySelector('.table-container');
    if (!tableContainer) {
      console.error('未找到表格容器,请添加 .table-container 类名');
      return;
    }
​
    // 创建底部拖拽手柄
    const lineEl = document.createElement('div');
    lineEl.style = `
      height: 5px; 
      width: 100%; 
      background: #f0f0f0; 
      position: absolute; 
      bottom: 0; 
      left: 0; 
      cursor: s-resize; /* 垂直调整光标 */
    `;
    // 鼠标悬停时显示深色提示
    lineEl.onmouseover = () => lineEl.style.background = '#e0e0e0';
    lineEl.onmouseout = () => lineEl.style.background = '#f0f0f0';
​
    // 绑定鼠标按下事件
    lineEl.addEventListener('mousedown', (e) => {
      e.preventDefault();
      // 记录鼠标按下时的Y坐标和容器当前高度
      const startY = e.clientY;
      const startHeight = tableContainer.offsetHeight;
​
      // 鼠标移动时调整高度
      const handleMouseMove = (e) => {
        e.preventDefault();
        // 计算高度变化量(限制最小高度300px,最大800px)
        const deltaY = e.clientY - startY;
        const newHeight = Math.max(300, Math.min(800, startHeight + deltaY));
        tableContainer.style.height = `${newHeight}px`;
      };
​
      // 鼠标松开时移除事件
      const handleMouseUp = () => {
        document.removeEventListener('mousemove', handleMouseMove);
        document.removeEventListener('mouseup', handleMouseUp);
      };
​
      // 绑定全局事件
      document.addEventListener('mousemove', handleMouseMove);
      document.addEventListener('mouseup', handleMouseUp);
    }, false);
​
    // 将手柄添加到表格容器(容器需设为相对定位)
    tableContainer.appendChild(lineEl);
  }
};

使用示例(TableDemo.vue


<template>
  <div class="table-page">
    <h3>可调整高度的表格</h3>
    <!-- 表格容器:相对定位,溢出时显示滚动条 -->
    <div class="table-container" v-tableHeightResize>
      <table border="1" width="100%" cellpadding="10">
        <thead>
          <tr><th>ID</th><th>名称</th><th>状态</th></tr>
        </thead>
        <tbody>
          <!-- 模拟100行数据 -->
          <tr v-for="i in 100" :key="i">
            <td>{{ i }}</td>
            <td>数据项 {{ i }}</td>
            <td>正常</td>
          </tr>
        </tbody>
      </table>
    </div>
  </div>
</template><style scoped>
.table-page {
  width: 800px;
  margin: 20px auto;
}
.table-container {
  position: relative; /* 确保手柄绝对定位生效 */
  height: 400px; /* 初始高度 */
  overflow: auto; /* 内容超出时显示滚动条 */
  border: 1px solid #ddd;
}
</style><script>
import tableHeightResize from '@/directive/tableHeightResize.js';
export default {
  directives: {
    tableHeightResize
  }
};
</script>

效果:拖拽表格底部灰色手柄,可上下调整容器高度(300px~800px),内容超出时自动显示滚动条。

案例3:图片透明度调整(可视化交互)

场景:图片预览页面中,用户可通过拖拽滑块直观调整图片透明度。

指令代码(directive/imgOpacityResize.js


export default {
  bind(el) {
    // 查找图片和控制区(需在模板中定义对应类名)
    const imgDom = el.querySelector('.target-img');
    const controlDom = el.querySelector('.opacity-controls');
    if (!imgDom || !controlDom) {
      console.error('未找到图片或控制区,请添加 .target-img 和 .opacity-controls 类名');
      return;
    }
​
    // 创建透明度滑块容器
    const sliderContainer = document.createElement('div');
    sliderContainer.style = `
      width: 200px; 
      height: 6px; 
      background: linear-gradient(to right, rgba(0,0,0,0), rgba(0,0,0,1)); 
      margin: 10px 0; 
      position: relative;
      border-radius: 3px;
    `;
​
    // 创建滑块按钮
    const sliderBtn = document.createElement('div');
    sliderBtn.style = `
      width: 16px; 
      height: 16px; 
      background: #409eff; 
      border-radius: 50%; 
      position: absolute; 
      top: 50%; 
      left: 50%; /* 初始在中间(透明度0.5) */
      transform: translate(-50%, -50%); 
      box-shadow: 0 0 3px rgba(0,0,0,0.3);
      cursor: pointer;
    `;
    sliderContainer.appendChild(sliderBtn);
​
    // 显示当前透明度值
    const valueText = document.createElement('span');
    valueText.style = 'margin-left: 10px; color: #666;';
    valueText.textContent = '透明度:50%';
​
    // 组合控制区
    controlDom.appendChild(sliderContainer);
    controlDom.appendChild(valueText);
​
    // 绑定鼠标按下事件
    sliderBtn.addEventListener('mousedown', (e) => {
      e.preventDefault();
      const sliderWidth = sliderContainer.offsetWidth;
      const btnWidth = sliderBtn.offsetWidth;
      // 计算滑块可移动的最大范围(容器宽 - 按钮宽)
      const maxLeft = sliderWidth - btnWidth;
​
      // 鼠标移动时调整滑块位置和透明度
      const handleMouseMove = (e) => {
        e.preventDefault();
        // 计算滑块相对容器的left值(限制在0~maxLeft之间)
        const sliderRect = sliderContainer.getBoundingClientRect();
        let left = e.clientX - sliderRect.left - btnWidth / 2;
        left = Math.max(0, Math.min(maxLeft, left));
​
        // 更新滑块位置
        sliderBtn.style.left = `${left}px`;
​
        // 计算透明度(0~1)并应用到图片
        const opacity = left / maxLeft;
        imgDom.style.opacity = opacity;
​
        // 更新显示文本
        valueText.textContent = `透明度:${Math.round(opacity * 100)}%`;
      };
​
      // 鼠标松开时移除事件
      const handleMouseUp = () => {
        document.removeEventListener('mousemove', handleMouseMove);
        document.removeEventListener('mouseup', handleMouseUp);
      };
​
      // 绑定全局事件
      document.addEventListener('mousemove', handleMouseMove);
      document.addEventListener('mouseup', handleMouseUp);
    }, false);
  }
};

使用示例(ImageDemo.vue


<template>
  <div class="image-container" v-imgOpacityResize>
    <h3>图片透明度调整</h3>
    <!-- 图片元素 -->
    <img 
      class="target-img" 
      src="https://picsum.photos/800/400" 
      alt="示例图片"
    >
    <!-- 控制区(滑块会被插入到这里) -->
    <div class="opacity-controls">
      <p>拖动滑块调整透明度:</p>
    </div>
  </div>
</template><style scoped>
.image-container {
  width: 800px;
  margin: 20px auto;
  text-align: center;
}
.target-img {
  max-width: 100%;
  border: 1px solid #eee;
  opacity: 0.5; /* 初始透明度 */
}
.opacity-controls {
  margin-top: 20px;
}
</style><script>
import imgOpacityResize from '@/directive/imgOpacityResize.js';
export default {
  directives: {
    imgOpacityResize
  }
};
</script>

效果:拖拽滑块时,图片透明度随滑块位置变化(左→右:透明→不透明),同时显示当前透明度百分比。

扩展逻辑总结

这三个案例均基于原指令的核心逻辑(手柄+鼠标事件+样式修改),通过以下调整实现不同功能:

  1. 手柄位置/样式:从弹窗右侧→分栏中间→表格底部→图片控制区,样式适配场景(垂直条→水平条→滑块)。
  2. 目标元素:从弹窗→分栏容器→表格容器→图片,只要能通过类名查找即可扩展。
  3. 修改的样式属性:从 widthwidth(联动)→heightopacity,覆盖尺寸和非尺寸调整。

根据实际需求,还可进一步扩展(如调整字体大小、边框粗细等),核心是保持“用户交互→事件监听→样式更新”的逻辑链。