拖拽演示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