拖拽演示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,这样就和我们拖拽后的 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。
略...
注意,要加上 v- ,即 v-dialogdrag。
使用 vue3 的自定义指令给 element-plus 的 el-dialog 增加拖拽功能
原文:https://blog.51cto.com/u_15179455/2824096