element-plus 提供的 el-dialog 对话框功能非常强大,只是美中不足不能通过拖拽的方式改变位置,有点小遗憾,那么怎么办呢?我们可以通过 vue 的自定义指令来实现一个可以拖拽的对话框(el-dialog)。

拖拽演示

https://www.zhihu.com/zvideo/1380450791975731200

vue3 的自定义指令 directive

为啥选择自定义指令的方式来实现呢?一个是可以方便的获得 dom 便于操作,另一个是方便使用和封装。

自定义指令有两种注册方式,一个是全局注册,一个是局部注册。

  • 全局注册自定义指令
app.directive('focus', {
// 当被绑定的元素插入到 DOM 中时……
mounted(el) {
// Focus the element
el.focus()
}
})

来自于官网。后面做插件的时候需要用到。

  • 局部注册自定义指令
directives: {
focus: {
// 指令的定义
mounted(el) {
el.focus()
}
}
}

在开发测试的阶段可以用这种方式,便于调试。插件每次修改的时候都会重新加载,而局部注册的只需要局部更新即可。

我们可以定义一个 dialogdrag,然后在 mounted 里面实现拖拽的功能。

分析 element-plus 的 Dialog 对话框

想要实现拖拽功能,首先要了解 Dialog 对话框渲染出来的结构,然后才好针对性下手改造。

通过分析可见如下结构:

简单的说,一个 div 里面放了三个 div,通过 margin(top、left) 来实现“居中”的效果。

那么也就是说我们想要实现拖拽功能的话,可以通过改变 margin-left、margin-top 的方式来。

鼠标的三个函数

提到拖拽功能,那么鼠标的三个事件 onmousedown、onmousemove、onmouseup 就必不可少了。

  • onmousedown

    鼠标按下的时候记录光标的坐标,进入拖拽状态。

  • onmouseup

    鼠标抬起的时候记录光标的坐标,结束拖拽状态。

  • onmousemove

    按住鼠标拖动的时候触发,计算光标的偏移量,修改对话框的 margin 实现拖拽的效果。

实现代码

本来想写一个通用一点的,但是对话框渲染出来的结构比较复杂,似乎也不够通用,所以先针对 el-dialog 实现拖拽功能。

  app.directive('dialogdrag', {
// 渲染完毕
mounted(el, binding) {
// binding.arg
// binding.value
// 可视窗口的宽度
const clientWidth = document.documentElement.clientWidth
// 可视窗口的高度
const clientHeight = document.documentElement.clientHeight
// 记录坐标
let domset = {
x: clientWidth / 4, // 默认width 50%
y: clientHeight * 15 / 100 // 根据 15vh 计算
} // 弹窗的容器
const domDrag = el.firstElementChild.firstElementChild
// 重新设置上、左距离
domDrag.style.marginTop = domset.y + 'px'
domDrag.style.marginLeft = domset.x + 'px' // 记录拖拽开始的光标坐标,0 表示没有拖拽
let start = { x: 0, y: 0 }
// 移动中记录偏移量
let move = { x: 0, y: 0 } // 鼠标按下,开始拖拽
domDrag.onmousedown = (e) => {
// 判断对话框是否重新打开
if (domDrag.style.marginTop === '15vh') {
// 重新打开,设置 domset.y top
domset.y = clientHeight * 15 / 100
}
start.x = e.clientX
start.y = e.clientY
domDrag.style.cursor = 'move' // 改变光标形状
} // 鼠标移动,实时跟踪
domDrag.onmousemove = (e) => {
if (start.x === 0) { // 不是拖拽状态
return
}
move.x = e.clientX - start.x
move.y = e.clientY - start.y // 初始位置 + 拖拽距离
domDrag.style.marginLeft = ( domset.x + move.x ) + 'px'
domDrag.style.marginTop = ( domset.y + move.y ) + 'px'
}
// 鼠标抬起,结束拖拽
domDrag.onmouseup = (e) => {
move.x = e.clientX - start.x
move.y = e.clientY - start.y // 记录新坐标,作为下次拖拽的初始位置
domset.x += move.x
domset.y += move.y
domDrag.style.cursor = '' // 恢复光标形状
domDrag.style.marginLeft = domset.x + 'px'
domDrag.style.marginTop = domset.y + 'px'
// 结束拖拽
start.x = 0
}
}
})
  • 重新定位对话框

    默认的 top 是15vh,也就是距离顶部 15% ,所以需要 用 clientHeight * 15 / 100 转换为像素。

    默认的 left 是 50%,所以需要用 clientWidth / 4 转换为像素。

这样修改有一个小问题,当窗口大小发生改变的时候,左距离不会随之改变。

  • 记录位置坐标和偏移量

    首先要记录对话框的距离,然后要记录拖拽的时候产生的偏移量。
  1. domset 可以记录对话框的初始坐标。
  2. start 可以记录开始拖拽的时候光标的位置。
  3. move 记录拖拽过程中,光标移动的偏移量。
  • 按下鼠标 onmousedown

    按下鼠标,表示开始拖拽,这时候需要我们记录光标的位置。

    另外在测试的时候发现一个小问题,当关闭对话框的时候有一个过渡动画,然后在打开对话框进行拖拽的时候,就飞掉了。

找了一下原因后发现,在关闭的过渡动画的时候,会把 top 改成 15vh,这样就和我们拖拽后的 top 不一致。

所以在按下鼠标的时候需要做一个判断。如果恢复了初始状态,那么需要改一下 domset 的 y 坐标。x坐标不用改,因为过渡动画没有改 left 。

  • 移动鼠标 onmousemove

    在移动鼠标的过程中,我们可以得到光标的位置,减去初始光标位置,就是对话框要移动的距离。

    然后我们用对话框的 初始坐标 + 偏移量,就可以得到对话框的新的位置坐标。

    这样就实现了对话框的拖拽。

  • 抬起鼠标 onmouseup

    不能一直拖拽,所以我们需要一个结束动作。

    当抬起鼠标的时候,我们可以认为是结束拖拽了,这时我们要记录对话框的新的位置坐标,

    然后设置 start.x = 0 表示结束拖拽。

做成插件便于复用

最后我们把这个拖拽功能做成一个插件,这样可以便于全局注册。

建立一个js文件

// dialogDrag.js

const dialogDrag = (app, options) => {
app.directive('dialogdrag', {
// 指令的定义
mounted(el, binding) {
同上,略...
} export default dialogDrag

然后在 main.js 里面挂载这个插件。

// 拖拽
import dialogDrag from './control-web/js/dialogDrag.js' createApp(App).use(dialogDrag) // 对话框的拖拽

使用方式

本来想直接放在 el-dialog 里面,但是却没有效果,所以只好在外面套上一个 div。

<div v-dialogdrag>
<el-dialog
title="收货地址"
v-model="dialogFormVisible"
:modal="false"
>
略...
</el-dialog>
</div>

注意,要加上 v- ,即 v-dialogdrag。

源码

https://gitee.com/naturefw/nf-vite2-element

/src/control-web/js/dialogDrag.js

https://gitee.com/naturefw/nf-vite2-element/tree/master/src/control-web/js

使用 vue3 的自定义指令给 element-plus 的 el-dialog 增加拖拽功能的更多相关文章

  1. VUE3 之 自定义指令的实现 - 这个系列的教程通俗易懂,适合新手

    1. 概述 老话说的好:能屈能伸的人生,才是完满而丰富的人生. 言归正传,今天我们来聊聊 VUE 中自定义指令的实现. 2. 自定义指令 2.1 文本框聚焦的实现  <body> < ...

  2. vue3.0自定义指令(drectives)

    在大多数情况下,你都可以操作数据来修改视图,或者反之.但是还是避免不了偶尔要操作原生 DOM,这时候,你就能用到自定义指令. 举个例子,你想让页面的文本框自动聚焦,在没有学习自定义指令的时候,我们可能 ...

  3. 使dialog可拖拽指令

    在项目开发过程中,需要支持dialog弹窗可拖拽,则需要对dialog添加指令.具体操作说明如下: (1)在用于存放指令的文件夹内,新建拖拽指令文件夹,例如命名为:el-dragDialog,如下所示 ...

  4. element穿梭框el-transfer增加拖拽排序和shift多选checkbox功能

    <template> <div class="demo"> <el-transfer v-model="value" filter ...

  5. vue自定义指令笔记

    https://cn.vuejs.org/v2/guide/custom-directive.html 在vue中,有时候我们会把抽象的方法封装为一个自定义指令,多个地方共用 比如:拖拽指令 < ...

  6. vue3 自定义指令控制按钮权限

    经过1个周的摸索和查阅资料,终于搞定VUE3中自定义指令,实现按钮级别的权限控制.当然,只是简单的对按钮进行隐藏和删除的dom操作比较容易,一直纠结的是当按钮无权限时,不是直接删除当前dom元素(bu ...

  7. Vue.js 源码分析(二十四) 高级应用 自定义指令详解

    除了核心功能默认内置的指令 (v-model 和 v-show),Vue 也允许注册自定义指令. 官网介绍的比较抽象,显得很高大上,我个人对自定义指令的理解是:当自定义指令作用在一些DOM元素或组件上 ...

  8. vue3系列:vue3.0自定义虚拟滚动条V3Scroll|vue3模拟滚动条组件

    基于Vue3.0构建PC桌面端自定义美化滚动条组件V3Scroll. 前段时间有分享一个Vue3 PC网页端弹窗组件,今天带来最新开发的Vue3.0版虚拟滚动条组件. V3Scroll 使用vue3. ...

  9. Angular17 Angular自定义指令

    1 什么是HTML HTML文档就是一个纯文本文件,该文件包含了HTML元素.CSS样式以及JavaScript代码:HTML元素是由标签呈现,浏览器会为每个标签创建带有属性的DOM对象,浏览器通过渲 ...

随机推荐

  1. vue全局错误捕获

    1.errorHandler Vue全局配置 errorHandler可以进行全局错误收集,捕获全局错误抛出,避免前端页面挂掉   export default function errorHandl ...

  2. 导出目录的JS代码,与目录的三级标题测试

    二级标题 三级标题 三级标题 三级标题 三级标题 三级标题 二级标题 三级标题 三级标题 三级标题 三级标题 三级标题 这里是现在页尾目录功能的代码源码: <!-- 目录索引列表生成 --> ...

  3. VisualGDB_VS2010_开发PHP扩展

    1.新建一个Linux项目

  4. 问题笔记 - element表格 操作状态值

    1.必须从传到表里的数据源中取值(scope.row.star)

  5. java面试一日一题:mysql事务是如何实现的

    问题:请讲下mysql的事务是如何实现的 分析:该问题主要考察对事务的理解及实现方式: 回答要点: 主要从以下几点去考虑, 1.对事务的概念的理解? 2.事务的实现方式? 讲到mysql的事务,很快可 ...

  6. Dynamics CRM邮箱配置

    Dynamics CRM对邮箱有很好的支持,开通邮箱后方便用户通过邮件进行Dynamics CRM的业务处理,同时也可以作为一直消息流提醒的手段应用于审批.通知等场景,可以做一些更深入的功能拓展. 本 ...

  7. 记一次metasploitable2内网渗透之samba服务的攻击

    80端口中对应一些web靶场,在这里不记录 111端口的利用是向rpcbind服务的UDP套接字发送60字节载荷,便可填充目标内存,搞崩主机系统.在这里也不记录 Samba服务简介 Samba是在Li ...

  8. c++ 实现向量去重操作

    去重的时候要考虑线性表或链表是否是有序 1.1.无序线性表 对于向量[1,5,3,7,2,4,7,3], 从头开始扫描vector内的元素, 对于表中r处的元素a[r], 检查数组0至r-1区间内是否 ...

  9. 吉特日化MES&WMS系统--三色灯控制协议转http

    关于硬件控制大部分都是使用CS客户端程序,一般连接口都是用网口,串口,USB口等,应用通讯是不支持HTTp协议操作的,而目前一般做技术的人员都在于BS开发,使用HTTP 协议,所以在硬件交互上可能觉得 ...

  10. JMeter 实战案例

    案例1:博客网站后端测试 案例2:JPetStore 应用 案例1:博客网站后端测试 测试目标 测试博客网站后端的常用 HTTP 接口的访问方法. 展示 HTTP 请求的各类使用方法. 展示提取 JS ...