最新国产好看的视频,伊人天堂AV在线,国产Aaaaaa视频,蜜臀视频在线观看一区,人妻av色图,密臀久久久精品影片,青青视频免费观看毛片,久草在线观看视,国产三级精品色情在线

Vue自定義指令實(shí)現(xiàn)內(nèi)容區(qū)拖拽調(diào)整大小

 更新時(shí)間:2025年12月23日 09:33:10   作者:酸菜土狗  
日常開發(fā)中經(jīng)常遇到需要手動(dòng)調(diào)整內(nèi)容區(qū)大小的場(chǎng)景,比如側(cè)邊欄、彈窗、報(bào)表面板等,下面我們就來看看Vue如何通過自定義指令實(shí)現(xiàn)內(nèi)容區(qū)拖拽調(diào)整大小吧

日常開發(fā)中經(jīng)常遇到需要手動(dòng)調(diào)整內(nèi)容區(qū)大小的場(chǎng)景,比如側(cè)邊欄、彈窗、報(bào)表面板等。分享一個(gè)我寫的「拖拽調(diào)整大小指令」,支持自定義最小尺寸、拖拽手柄樣式,能監(jiān)聽尺寸變化

先看效果

核心代碼解析

指令文件 directives/resizable-full.js ,關(guān)鍵部分:

1. 指令鉤子:初始化 + 更新 + 清理

Vue 指令的 3 個(gè)核心鉤子,保證指令的生命周期完整:

export default {
  bind(el, binding) {
    // 指令綁定時(shí)初始化拖拽功能
    initResizable(el, binding);
  },
  update(el, binding) {
    // 禁用狀態(tài)變化時(shí),重新初始化
    if (binding.value?.disabled !== binding.oldValue?.disabled) {
      cleanupResizable(el); // 先清理舊的
      initResizable(el, binding); // 再初始化新的
    }
  },
  unbind(el) {
    // 指令解綁時(shí),清理所有手柄和事件(避免內(nèi)存泄漏)
    cleanupResizable(el);
  }
};

2. 初始化拖拽:創(chuàng)建手柄 + 核心邏輯

initResizable 是核心函數(shù),主要做 2 件事:創(chuàng)建拖拽手柄、寫拖拽邏輯。

創(chuàng)建拖拽手柄

我只保留了「右下角」的拖拽手柄(其他方向注釋掉了,需要的話自己解開),樣式可自定義:

// 定義手柄配置(只留了bottom-right)
const handles = [
  { dir: 'bottom-right', style: { bottom: 0, right: 0, cursor: 'nwse-resize' } }
];

// 循環(huán)創(chuàng)建手柄元素
handles.forEach(handleConf => {
  const handle = document.createElement('div');
  handle.className = `resizable-handle resizable-handle--${handleConf.dir}`;
  handle.dataset.dir = handleConf.dir;
  
  // 手柄樣式:小方塊、半透明、hover高亮
  Object.assign(handle.style, {
    position: 'absolute',
    width: `${handleSize}px`,
    height: `${handleSize}px`,
    background: handleColor,
    opacity: '0.6',
    zIndex: 999,
    transition: 'opacity 0.2s',
    ...handleConf.style
  });

  // hover時(shí)手柄高亮
  handle.addEventListener('mouseenter', () => handle.style.opacity = '1');
  handle.addEventListener('mouseleave', () => handle.style.opacity = '0.6');

  el.appendChild(handle); // 把手柄加到目標(biāo)元素上
  el._resizableConfig.handles.push(handle); // 存起來方便后續(xù)清理
});

拖拽核心邏輯

分 3 步:按下鼠標(biāo)(記錄初始狀態(tài))→ 移動(dòng)鼠標(biāo)(計(jì)算新尺寸)→ 松開鼠標(biāo)(觸發(fā)回調(diào) + 清理):

// 1. 按下鼠標(biāo):記錄初始位置和尺寸
const mouseDownHandler = (e) => {
  const handle = e.target.closest('.resizable-handle');
  if (!handle) return;

  e.stopPropagation();
  e.preventDefault();
  
  const dir = handle.dataset.dir;
  const rect = el.getBoundingClientRect(); // 獲取元素當(dāng)前位置和尺寸

  // 存初始狀態(tài):鼠標(biāo)位置、元素尺寸/位置
  startState = {
    dir,
    startX: e.clientX,
    startY: e.clientY,
    startWidth: rect.width,
    startHeight: rect.height
  };

  // 綁定移動(dòng)/松開事件(綁在document上,避免拖拽時(shí)鼠標(biāo)移出元素失效)
  document.addEventListener('mousemove', onMouseMove);
  document.addEventListener('mouseup', onMouseUp);
};

// 2. 移動(dòng)鼠標(biāo):計(jì)算新寬高并賦值
const onMouseMove = (e) => {
  if (!startState) return;
  const { dir, startX, startY, startWidth, startHeight } = startState;
  let newWidth = startWidth;
  let newHeight = startHeight;

  // 只處理右下角拖拽:寬高都增加
  if (dir === 'bottom-right') {
    newWidth = startWidth + (e.clientX - startX);
    newHeight = startHeight + (e.clientY - startY);
  }

  // 限制最小寬高(避免拖到太?。?
  newWidth = Math.max(minWidth, newWidth);
  newHeight = Math.max(minHeight, newHeight);

  // 給元素設(shè)置新尺寸
  el.style.width = `${newWidth}px`;
  el.style.height = `${newHeight}px`;
};

// 3. 松開鼠標(biāo):觸發(fā)回調(diào)+清理事件
const onMouseUp = () => {
  // 拖拽結(jié)束,觸發(fā)自定義回調(diào),返回最新尺寸
  if (startState && el._resizableConfig.onResize) {
    el._resizableConfig.onResize({
      width: parseInt(el.style.width),
      height: parseInt(el.style.height)
    });
  }
  startState = null;
  // 移除事件(避免重復(fù)綁定)
  document.removeEventListener('mousemove', onMouseMove);
  document.removeEventListener('mouseup', onMouseUp);
};

// 給元素綁定按下事件
el.addEventListener('mousedown', mouseDownHandler);

3. 清理函數(shù):避免內(nèi)存泄漏

cleanupResizable 負(fù)責(zé)移除所有手柄元素和事件監(jiān)聽器,指令解綁時(shí)必執(zhí)行:

function cleanupResizable(el) {
  if (el._resizableConfig) {
    // 移除所有手柄
    el._resizableConfig.handles.forEach(handle => {
      if (handle.parentNode === el) el.removeChild(handle);
    });
    // 移除所有事件監(jiān)聽器
    el.removeEventListener('mousedown', el._resizableConfig.mouseDownHandler);
    document.removeEventListener('mousemove', el._resizableConfig.mouseMoveHandler);
    document.removeEventListener('mouseup', el._resizableConfig.mouseUpHandler);
    // 刪除配置(釋放內(nèi)存)
    delete el._resizableConfig;
  }
}

如何使用

1.全局注冊(cè)指令(main.js):

import resizableFull from './directives/resizable-full';
Vue.directive('resizable-full', resizableFull);

2.頁(yè)面中使用

<template>
  <!-- 給需要拖拽的元素加指令 -->
  <div 
    v-resizable-full="{
      minWidth: 300, // 最小寬度
      minHeight: 200, // 最小高度
      handleSize: 10, // 手柄大小
      handleColor: '#409eff', // 手柄顏色
      onResize: handleResize // 拖拽結(jié)束回調(diào)
    }"
    style="position: relative; width: 400px; height: 300px; border: 1px solid #eee;"
  >
    我是可拖拽調(diào)整大小的內(nèi)容區(qū)~
  </div>
</template>

<script>
export default {
  methods: {
    // 拖拽結(jié)束,拿到最新尺寸
    handleResize({ width, height }) {
      console.log('新尺寸:', width, height);
    }
  }
};
</script>

關(guān)鍵注意點(diǎn)(避坑)

目標(biāo)元素必須設(shè) position: relative/absolute/fixed:因?yàn)槭直墙^對(duì)定位,依賴父元素的定位;

事件綁在 document 上:拖拽時(shí)鼠標(biāo)可能移出目標(biāo)元素,綁在 document 上才不會(huì)斷;

一定要清理事件 / 元素:指令解綁時(shí)執(zhí)行 cleanupResizable,避免內(nèi)存泄漏;

最小尺寸限制:通過 minWidth/minHeight 避免元素被拖到太小,影響體驗(yàn)。

擴(kuò)展玩法

解開注釋的其他 7 個(gè)方向手柄,實(shí)現(xiàn)全方向拖拽;

給手柄加 hover 提示(比如 “拖拽調(diào)整大小”);

支持拖拽時(shí)實(shí)時(shí)觸發(fā)回調(diào)(不止結(jié)束時(shí));

自定義手柄樣式(比如改成虛線、加圖標(biāo))。

總結(jié)

這個(gè)自定義指令核心是「創(chuàng)建拖拽手柄 + 監(jiān)聽鼠標(biāo)事件 + 計(jì)算尺寸變化」,邏輯不復(fù)雜,可以根據(jù)自己的業(yè)務(wù)場(chǎng)景定制。親測(cè)報(bào)表和彈窗都很適用~

以上就是Vue自定義指令實(shí)現(xiàn)內(nèi)容區(qū)拖拽調(diào)整大小的詳細(xì)內(nèi)容,更多關(guān)于Vue拖拽調(diào)整內(nèi)容區(qū)大小的資料請(qǐng)關(guān)注腳本之家其它相關(guān)文章!

相關(guān)文章

最新評(píng)論

苏尼特左旗| 宜宾县| 全州县| 石棉县| 临城县| 紫金县| 乌拉特后旗| 公安县| 青海省| 白银市| 黔东| 大邑县| 昌吉市| 屏南县| 昌黎县| 方山县| 加查县| 林芝县| 丰宁| 云南省| 卓尼县| 昭觉县| 马公市| 福泉市| 独山县| 荆州市| 讷河市| 绥棱县| 文安县| 都江堰市| 岚皋县| 永平县| 南召县| 宜川县| 云龙县| 容城县| 泉州市| 江川县| 阿尔山市| 上饶县| 襄城县|