|
|
|
|
@ -0,0 +1,223 @@
|
|
|
|
|
## 代码详解
|
|
|
|
|
|
|
|
|
|
当然可以,下面是对这段 Vue.js 自定义指令(`updated` 钩子)代码的**逐行详细解释**,帮助你全面理解其功能、原理和实现细节。
|
|
|
|
|
|
|
|
|
|
---
|
|
|
|
|
|
|
|
|
|
## 🎯 **整体功能**
|
|
|
|
|
这段代码是一个 Vue 的自定义指令(或更准确地说,是一个 **组件的 `updated` 生命周期钩子**),用于:
|
|
|
|
|
> **让 `el-dialog` 组件支持“拖拽移动”和“右下角缩放”**。
|
|
|
|
|
|
|
|
|
|
它通过原生 DOM 操作,在 `el-dialog` 被更新后,动态添加拖拽和缩放功能,使其像桌面窗口一样可拖动、可调整大小。
|
|
|
|
|
|
|
|
|
|
---
|
|
|
|
|
|
|
|
|
|
## ✅ 一、核心结构:`updated(el)`
|
|
|
|
|
```js
|
|
|
|
|
updated(el) {
|
|
|
|
|
```
|
|
|
|
|
- `updated` 是 Vue 组件的生命周期钩子,当组件的 VDOM 更新后执行。
|
|
|
|
|
- `el` 是该指令作用的 DOM 元素(即 `<el-dialog>` 的根节点)。
|
|
|
|
|
- 这里利用 `updated` 在 DOM 渲染完成后注入行为,确保元素存在。
|
|
|
|
|
|
|
|
|
|
---
|
|
|
|
|
|
|
|
|
|
## ✅ 二、定义最小尺寸
|
|
|
|
|
```js
|
|
|
|
|
let minWidth = 600;
|
|
|
|
|
let minHeight = 200;
|
|
|
|
|
```
|
|
|
|
|
- 设置对话框的**最小宽度和高度**,防止用户把窗口拖得太小。
|
|
|
|
|
- 保证用户体验:不会出现“看不见内容”的情况。
|
|
|
|
|
|
|
|
|
|
---
|
|
|
|
|
|
|
|
|
|
## ✅ 三、获取关键 DOM 元素
|
|
|
|
|
```js
|
|
|
|
|
const dialogHeaderEl = el.querySelector('.el-dialog__header');
|
|
|
|
|
const dragDom = el.querySelector('.el-dialog');
|
|
|
|
|
```
|
|
|
|
|
- `dialogHeaderEl`:对话框的标题栏区域,用于拖拽移动。
|
|
|
|
|
- `dragDom`:整个对话框容器(`.el-dialog`),用于移动和缩放操作。
|
|
|
|
|
- 如果没有找到 `dragDom`,直接 `return`,避免报错。
|
|
|
|
|
|
|
|
|
|
---
|
|
|
|
|
|
|
|
|
|
## ✅ 四、设置样式与禁用选择
|
|
|
|
|
```js
|
|
|
|
|
dragDom.style.overflow = 'hidden';
|
|
|
|
|
dialogHeaderEl.onselectstart = () => false;
|
|
|
|
|
dialogHeaderEl.style.cursor = 'move';
|
|
|
|
|
```
|
|
|
|
|
- `overflow: hidden`:防止拖拽过程中出现滚动条或内容溢出。
|
|
|
|
|
- `onselectstart = () => false`:禁止在标题栏上选中文本(避免误选)。
|
|
|
|
|
- `cursor: move`:鼠标悬停在标题栏时显示“移动”光标,提升交互提示。
|
|
|
|
|
|
|
|
|
|
---
|
|
|
|
|
|
|
|
|
|
## ✅ 五、获取当前样式(兼容 IE 和现代浏览器)
|
|
|
|
|
```js
|
|
|
|
|
const sty = dragDom.currentStyle || window.getComputedStyle(dragDom, null);
|
|
|
|
|
```
|
|
|
|
|
- `currentStyle`:IE 浏览器使用。
|
|
|
|
|
- `getComputedStyle`:现代浏览器使用。
|
|
|
|
|
- 获取当前 `dragDom` 的 `left`、`top` 等样式值,用于后续计算偏移量。
|
|
|
|
|
|
|
|
|
|
---
|
|
|
|
|
|
|
|
|
|
## ✅ 六、拖拽移动功能(核心逻辑)
|
|
|
|
|
```js
|
|
|
|
|
let moveDown = (e) => {
|
|
|
|
|
const disX = e.clientX - dialogHeaderEl.offsetLeft;
|
|
|
|
|
const disY = e.clientY - dialogHeaderEl.offsetTop;
|
|
|
|
|
```
|
|
|
|
|
- `moveDown` 是鼠标按下时的回调函数。
|
|
|
|
|
- `disX`, `disY`:记录鼠标点击位置与标题栏左上角的偏移量(即“相对偏移”)。
|
|
|
|
|
- 这是实现“拖拽”的关键:鼠标在移动时,用这个偏移量计算新位置。
|
|
|
|
|
|
|
|
|
|
```js
|
|
|
|
|
let styL = +sty.left.replace(/px/g, '');
|
|
|
|
|
let styT = +sty.top.replace(/px/g, '');
|
|
|
|
|
```
|
|
|
|
|
- 把 `left` 和 `top` 的字符串值(如 `"200px"`)转成数字(如 `200`)。
|
|
|
|
|
- 用于后续计算:新位置 = 鼠标位置 - 偏移量 + 原始位置。
|
|
|
|
|
|
|
|
|
|
```js
|
|
|
|
|
document.onmousemove = function (e) {
|
|
|
|
|
const l = e.clientX - disX;
|
|
|
|
|
const t = e.clientY - disY;
|
|
|
|
|
dragDom.style.left = `${l + styL}px`;
|
|
|
|
|
dragDom.style.top = `${t + styT}px`;
|
|
|
|
|
};
|
|
|
|
|
```
|
|
|
|
|
- `onmousemove`:鼠标移动时触发。
|
|
|
|
|
- 计算新位置:`新 left = 鼠标 X - 偏移 X + 原始 left`
|
|
|
|
|
- 动态设置 `left` 和 `top`,实现拖拽移动。
|
|
|
|
|
|
|
|
|
|
```js
|
|
|
|
|
document.onmouseup = () => {
|
|
|
|
|
document.onmousemove = null;
|
|
|
|
|
document.onmouseup = null;
|
|
|
|
|
};
|
|
|
|
|
```
|
|
|
|
|
- `onmouseup`:鼠标松开时,清除事件监听,防止内存泄漏。
|
|
|
|
|
|
|
|
|
|
```js
|
|
|
|
|
dialogHeaderEl.onmousedown = moveDown;
|
|
|
|
|
```
|
|
|
|
|
- 将 `moveDown` 绑定到标题栏的 `mousedown` 事件,启动拖拽。
|
|
|
|
|
|
|
|
|
|
---
|
|
|
|
|
|
|
|
|
|
## ✅ 七、创建缩放 handle(右下角拖拽)
|
|
|
|
|
```js
|
|
|
|
|
function createResizeHandle(cursor, styles, onMouseMoveHandler) {
|
|
|
|
|
const handle = document.createElement('div');
|
|
|
|
|
Object.assign(handle.style, {
|
|
|
|
|
position: 'absolute',
|
|
|
|
|
zIndex: '99',
|
|
|
|
|
...styles,
|
|
|
|
|
cursor,
|
|
|
|
|
});
|
|
|
|
|
dragDom.appendChild(handle);
|
|
|
|
|
handle.onmousedown = (e) => {
|
|
|
|
|
document.onmousemove = (ev) => {
|
|
|
|
|
ev.preventDefault();
|
|
|
|
|
onMouseMoveHandler(ev, e);
|
|
|
|
|
};
|
|
|
|
|
document.onmouseup = () => {
|
|
|
|
|
document.onmousemove = null;
|
|
|
|
|
document.onmouseup = null;
|
|
|
|
|
};
|
|
|
|
|
};
|
|
|
|
|
}
|
|
|
|
|
```
|
|
|
|
|
- **封装一个创建“缩放句柄”的工具函数**。
|
|
|
|
|
- `cursor`:光标样式(如 `se-resize` 表示右下角可缩放)。
|
|
|
|
|
- `styles`:CSS 样式,比如宽高、位置。
|
|
|
|
|
- `onMouseMoveHandler`:鼠标移动时的回调函数,用于处理缩放逻辑。
|
|
|
|
|
- 创建一个 `div` 作为缩放手柄,添加到 `dragDom` 中。
|
|
|
|
|
- 手柄在 `mousedown` 时绑定 `mousemove` 和 `mouseup`,实现缩放。
|
|
|
|
|
|
|
|
|
|
---
|
|
|
|
|
|
|
|
|
|
## ✅ 八、添加右下角缩放手柄
|
|
|
|
|
```js
|
|
|
|
|
createResizeHandle(
|
|
|
|
|
'se-resize',
|
|
|
|
|
{
|
|
|
|
|
width: '10px',
|
|
|
|
|
height: '10px',
|
|
|
|
|
right: '0px',
|
|
|
|
|
bottom: '0px',
|
|
|
|
|
},
|
|
|
|
|
(ev, e) => {
|
|
|
|
|
let width = ev.clientX - dragDom.getBoundingClientRect().left;
|
|
|
|
|
let height = ev.clientY - dragDom.getBoundingClientRect().top;
|
|
|
|
|
dragDom.style.width = width > minWidth ? width + 'px' : minWidth + 'px';
|
|
|
|
|
dragDom.style.height = height > minHeight ? height + 'px' : minHeight + 'px';
|
|
|
|
|
}
|
|
|
|
|
);
|
|
|
|
|
```
|
|
|
|
|
- 在对话框**右下角**添加一个 `10px × 10px` 的缩放手柄。
|
|
|
|
|
- `se-resize`:表示“右下角可缩放”。
|
|
|
|
|
- `right: 0px`, `bottom: 0px`:定位在右下角。
|
|
|
|
|
- `onMouseMoveHandler` 中:
|
|
|
|
|
- `getBoundingClientRect().left/top`:获取对话框当前左上角坐标。
|
|
|
|
|
- `ev.clientX - left`:计算当前宽度。
|
|
|
|
|
- `ev.clientY - top`:计算当前高度。
|
|
|
|
|
- 限制最小宽高:`width > minWidth`,避免窗口过小。
|
|
|
|
|
|
|
|
|
|
---
|
|
|
|
|
|
|
|
|
|
## ✅ 九、总结:功能一览
|
|
|
|
|
| 功能 | 实现方式 |
|
|
|
|
|
|------|----------|
|
|
|
|
|
| ✅ 拖拽移动 | 标题栏 `mousedown`,通过 `clientX/Y` 和偏移量计算新位置 |
|
|
|
|
|
| ✅ 禁止选中文本 | `onselectstart = () => false` |
|
|
|
|
|
| ✅ 右下角缩放 | 创建 `div` 手柄,监听 `mousemove` 动态修改 `width` 和 `height` |
|
|
|
|
|
| ✅ 最小尺寸限制 | 通过 `minWidth` / `minHeight` 保护窗口不被缩放过小 |
|
|
|
|
|
| ✅ 防止样式溢出 | `overflow: hidden` |
|
|
|
|
|
|
|
|
|
|
---
|
|
|
|
|
|
|
|
|
|
## ✅ 十、使用场景建议
|
|
|
|
|
- 适合用于 **需要自定义窗口行为的弹窗**,比如:
|
|
|
|
|
- 自定义的“设置面板”
|
|
|
|
|
- “可视化编辑器”中的浮动窗口
|
|
|
|
|
- “调试工具”弹窗
|
|
|
|
|
- 但注意:
|
|
|
|
|
- 不建议在移动端使用(拖拽体验差)。
|
|
|
|
|
- 不能用于 `v-model` 控制的普通弹窗,需配合 `v-if` 或 `v-show` 确保 DOM 存在。
|
|
|
|
|
|
|
|
|
|
---
|
|
|
|
|
|
|
|
|
|
## ✅ 十一、可扩展性建议
|
|
|
|
|
你可以在 `createResizeHandle` 中添加更多方向的手柄:
|
|
|
|
|
```js
|
|
|
|
|
// 例如:添加右上角、左下角、左上角缩放
|
|
|
|
|
createResizeHandle('ne-resize', { width: '10px', height: '10px', right: '0', top: '0' }, ...);
|
|
|
|
|
createResizeHandle('sw-resize', { width: '10px', height: '10px', left: '0', bottom: '0' }, ...);
|
|
|
|
|
createResizeHandle('nw-resize', { width: '10px', height: '10px', left: '0', top: '0' }, ...);
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
---
|
|
|
|
|
|
|
|
|
|
## ✅ 十二、注意事项
|
|
|
|
|
1. **需确保 `el-dialog` 的 `class` 为 `.el-dialog`**,否则 `querySelector` 会失败。
|
|
|
|
|
2. **该代码在 `updated` 钩子中执行,仅当组件更新时生效**,若对话框是动态渲染,需确保 `updated` 被触发。
|
|
|
|
|
3. **建议封装为一个全局指令**,以便复用:
|
|
|
|
|
```js
|
|
|
|
|
Vue.directive('drag-dialog', { updated });
|
|
|
|
|
```
|
|
|
|
|
然后在模板中使用:
|
|
|
|
|
```vue
|
|
|
|
|
<el-dialog v-drag-dialog ...>
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
---
|
|
|
|
|
|
|
|
|
|
## ✅ 总结一句话:
|
|
|
|
|
> 这段代码通过原生 DOM 操作,在 `el-dialog` 上动态添加“可拖拽移动”和“右下角缩放”功能,实现类似桌面窗口的交互体验,适用于需要高度自定义弹窗行为的场景。
|
|
|
|
|
|
|
|
|
|
如有需要,我可以帮你将其封装成一个可复用的 Vue 指令或组件。
|