欢迎来到尧图网

客户服务 关于我们

您的位置:首页 > 新闻 > 国际 > Element-UI中el-dialog弹框在Vue中如何实现浏览器可见区域拖拽功能

Element-UI中el-dialog弹框在Vue中如何实现浏览器可见区域拖拽功能

2025/6/18 17:20:33 来源:https://blog.csdn.net/jiciqiang/article/details/148599473  浏览:    关键词:Element-UI中el-dialog弹框在Vue中如何实现浏览器可见区域拖拽功能

        Elemenut-UI中会发现el-dialog弹框是没有拖拽功能,而在很多项目中是需要这一功能的;在Vue中,可以使用Vue.directive钩子函数,从底层并通过添加标签属性方式,来实现这一功能。

        在之前一篇文章中,也写过el-dialog弹框的拖拽功能,因通过position定位方式来实现,所以实现效果不算很好;这一篇,将通过transform动画方式完成弹框拖拽移位,并将其限定在浏览器可见区域的拖拽功能。

一、创建页面

        在src/views目录中,创建Dialog/index.vue文件,并在router中添加该页面路径。代码如下:

<template><div style="padding: 30px;"><el-button type="primary" size="mini" @click="() => visible = !visible">显示</el-button><el-dialog :visible.sync="visible" width="500px" title="标题"><div style="min-height: 200px;">这是一个可拖拽的弹框,并且限定在浏览器可限定区域显示。</div></el-dialog></div>
</template>
<script>
export default {data() {return {visible: false}}
}
</script>

        页面效果如下:

二、自定义指令

        Vue 2 的自定义指令提供了以下生命周期钩子:

  • bind:只调用一次,指令第一次绑定到元素时调用。在这里可以进行一次性的初始化设置。
  • inserted:被绑定元素插入父节点时调用(仅保证父节点存在,但不一定已被插入文档中)。
  • update:所在组件的 VNode 更新时调用,但可能发生在其子 VNode 更新之前。指令的值可能发生了改变,也可能没有。
  • componentUpdated:指令所在组件的 VNode 及其子 VNode 全部更新后调用。
  • unbind:只调用一次,指令与元素解绑时调用。

        以下是一个简单的自定义指令示例,用于在元素插入 DOM 后自动获取焦点。

<template><div><input v-focus /></div>
</template><script>
export default {directives: {focus: {// 当被绑定的元素插入到 DOM 中时…inserted(el) {// 聚焦元素el.focus();}}}
};
</script>

三、全局定义指令

        创建src/directives/index.js文件,定义全局自定义指令。示例代码如下:

import Vue from 'vue'
/*** 定义全局 自定义指令*/
Vue.directive('dragDialog', {inserted(el){// 初始化样式},componentUpdated(el){// 恢复原位置},bind(el){// 定义绑定事件}
})

        此时在main.js引入自定义指令,代码如下:

// The Vue build version to load with the `import` command
// (runtime-only or standalone) has been set in webpack.base.conf with an alias.
import Vue from 'vue'
import App from './App'
import router from './router'
import ElementUI from 'element-ui'
import 'element-ui/lib/theme-chalk/index.css'
// 引入指令
import '@/directives/index'Vue.use(ElementUI);Vue.config.productionTip = false/* eslint-disable no-new */
new Vue({el: '#app',router,components: { App },template: '<App/>'
})

        在el-dialog标签上,添加自定义可拖拽的指令。代码如下:

<template><div style="padding: 30px;"><el-button type="primary" size="mini" @click="() => visible = !visible">显示</el-button><el-dialog :visible.sync="visible" width="500px" title="标题" v-dragDialog><div style="min-height: 200px;">这是一个可拖拽的弹框,并且限定在浏览器可限定区域显示。</div></el-dialog></div>
</template>
<script>
export default {data() {return {visible: false}}
}
</script>

四、实现拖拽功能

        自定义指令可对DOM的操作,实现节点元素的查询、位置的动态调整、事件的定义等。通过自定义指令,可以在 Vue 2 中实现许多强大的功能。

4.1 默认样式

        首先,在inserted中查询el-dialog__header的DOM节点元素,在弹框头部样式中设置鼠标样式、内容禁止选择等。

        示例代码如下:

Vue.directive('dragDialog', {inserted(el){const header = el.querySelector('.el-dialog__header')// 初始化样式header.style.cursor = 'pointer'     // 鼠标更换为 小手图标header.style.userSelect = 'none'    // 禁止选择内容},componentUpdated(el){// 恢复原位置},bind(el){// 定义绑定事件}
})

4.2 绑定事件

        自定义指令bind只调用一次,所以事件的相关初始化操作,全部在这里进行定义。

        首先,使用querySelector()方法查询出弹框(el-dialog)和弹框标题区域(el-dialog)的DOM节点元素,然后通过style的getPropertyValue()属性方法,查询出el-dialog的transform位置信息,并初始化鼠标移动时,所需的position位置信息。 

        在鼠标按下时,添加document监听事件(移动和放开事件),通过mousemove移动事件,实时计算出弹框的位置数据,并重置transform的x,y轴数据。

        当鼠标放开时,移出mousemove和mouseup事件。

        代码如下:

import Vue from 'vue'
/*** 定义全局 自定义指令*/
Vue.directive('dragDialog', {inserted(el) {const header = el.querySelector('.el-dialog__header')const _dialog = el.querySelector('.el-dialog')// 初始化样式header.style.cursor = 'pointer'     // 鼠标更换为 小手图标header.style.userSelect = 'none'    // 禁止选择内容_dialog.style.transform = 'translate(0px, 0px)'},bind(el) {// 查询DOM 和 transformconst _dialog = el.querySelector('.el-dialog')const _header = el.querySelector('.el-dialog__header')// 初始化let _pos = {left: 0,top: 0,x: 0,y: 0}// 设置移动样式_header.style.cursor = 'move'// 鼠标移动事件的回调函数const mousemoveCallback = (e) => {// 计算位置let dx = e.clientX - _pos.xlet dy = e.clientY - _pos.y// 设置 位置_dialog.style.transform = `translate(${dx}px, ${dy}px)`}// 放开鼠标 事件的回调函数const mouseupCallback = () => {document.removeEventListener('mousemove', mousemoveCallback)document.removeEventListener('mouseup', mouseupCallback)}// 定义绑定事件_header.addEventListener('mousedown', (e) => {// 获取transformconst _transform = _dialog.style.getPropertyValue('transform')const _matrix = new DOMMatrix(_transform)// 初始位置 _pos = {left: _matrix.m41,top: _matrix.m42,x: e.clientX,y: e.clientY}// 添加事件document.addEventListener('mousemove', mousemoveCallback) // 移动事件document.addEventListener('mouseup', mouseupCallback) // 放开事件})//  end}
})

        以上功能完成后,弹框则可以灵活的被拖拽到浏览器中任何位置。

4.3 限定范围

        如果希望弹框在浏览器可视区域范围内移动,则可以如下图,在mousedown事件点击时,计算出el-dialog弹框四周可移动距离。

        代码如下:

import Vue from 'vue'
/*** 定义全局 自定义指令*/
Vue.directive('dragDialog', {inserted(el) {const header = el.querySelector('.el-dialog__header')const _dialog = el.querySelector('.el-dialog')// 初始化样式header.style.cursor = 'pointer'     // 鼠标更换为 小手图标header.style.userSelect = 'none'    // 禁止选择内容_dialog.style.transform = 'translate(0px, 0px)'},bind(el) {// 查询DOM 和 transformconst _dialog = el.querySelector('.el-dialog')const _header = el.querySelector('.el-dialog__header')// 初始化let _pos = {left: 0,top: 0,x: 0,y: 0}// 设置移动样式_header.style.cursor = 'move'// 鼠标移动事件的回调函数const mousemoveCallback = (e) => {// 计算位置let dx = e.clientX - _pos.xlet dy = e.clientY - _pos.y// 限定左侧位置if (dx <= -_pos.limitX) {dx = -_pos.limitX}// 限定右侧位置 else if (dx >= _pos.limitX) {dx = _pos.limitX}// 限定顶部位置 if (dy <= -_dialog.offsetTop) {dy = -_dialog.offsetTop}// 限定底部位置 else if (dy >= _pos.limitY) {dy = _pos.limitY}// 设置 位置_dialog.style.transform = `translate(${dx}px, ${dy}px)`}// 放开鼠标 事件的回调函数const mouseupCallback = () => {document.removeEventListener('mousemove', mousemoveCallback)document.removeEventListener('mouseup', mouseupCallback)}// 定义绑定事件_header.addEventListener('mousedown', (e) => {// 获取transformconst _transform = _dialog.style.getPropertyValue('transform')const _matrix = new DOMMatrix(_transform)// 计算边缘const limitX = (el.offsetWidth - _dialog.offsetWidth) / 2const limitY= el.offsetHeight - _dialog.offsetTop - _dialog.offsetHeight// 初始位置 _pos = {left: _matrix.m41,top: _matrix.m42,x: e.clientX,y: e.clientY,limitX: Math.abs(limitX),limitY: Math.abs(limitY)}// 添加事件document.addEventListener('mousemove', mousemoveCallback) // 移动事件document.addEventListener('mouseup', mouseupCallback) // 放开事件})// mousedown end}
})

        此时,再拖拽弹框,则无法拖出浏览器可视区域了。

4.4 恢复位置

        但是,如果弹框被关闭后,重新打开,则会发现弹框会在上次移动的位置,而没有恢复到浏览器居中位置。那么,则需要在componentUpdated组件完成更新时,判断transform中x,y位置是否为0。代码如下:

import Vue from 'vue'
/*** 定义全局 自定义指令*/
Vue.directive('dragDialog', {inserted(el) {const header = el.querySelector('.el-dialog__header')const _dialog = el.querySelector('.el-dialog')// 初始化样式header.style.cursor = 'pointer'     // 鼠标更换为 小手图标header.style.userSelect = 'none'    // 禁止选择内容_dialog.style.transform = 'translate(0px, 0px)'},componentUpdated(el) {// 查询DOM 和 transformconst _dialog = el.querySelector('.el-dialog')const _transform = _dialog.style.getPropertyValue('transform')const _matrix = new DOMMatrix(_transform)// 恢复原位置if (_matrix.m41 != 0 || _matrix.m42 != 0)_dialog.style.transform = 'translate(0px, 0px)'},bind(el) {// 略...}
})

4.5 Bug修复

        当el-dialog弹框在初始位置时,transform的x,y轴都为0,所以在计算新位置时,是没有问题的。但是,当弹框被移动后,则重新点击移动,位置会移置初始位置。解决这问题很简单,在计算新位置时,加上x,y轴移动后的位置路径即可。

        代码如下:

import Vue from 'vue'
/*** 定义全局 自定义指令*/
Vue.directive('dragDialog', {inserted(el) {const header = el.querySelector('.el-dialog__header')const _dialog = el.querySelector('.el-dialog')// 初始化样式header.style.cursor = 'pointer'     // 鼠标更换为 小手图标header.style.userSelect = 'none'    // 禁止选择内容_dialog.style.transform = 'translate(0px, 0px)'},componentUpdated(el) {// 查询DOM 和 transformconst _dialog = el.querySelector('.el-dialog')const _transform = _dialog.style.getPropertyValue('transform')const _matrix = new DOMMatrix(_transform)// 恢复原位置if (_matrix.m41 != 0 || _matrix.m42 != 0)_dialog.style.transform = 'translate(0px, 0px)'},bind(el) {// 查询DOM 和 transformconst _dialog = el.querySelector('.el-dialog')const _header = el.querySelector('.el-dialog__header')// 初始化let _pos = {left: 0,top: 0,x: 0,y: 0}// 设置移动样式_header.style.cursor = 'move'// 鼠标移动事件的回调函数const mousemoveCallback = (e) => {// 计算位置let dx = e.clientX - _pos.x + _pos.leftlet dy = e.clientY - _pos.y + _pos.top// 限定左侧位置if (dx <= -_pos.limitX) {dx = -_pos.limitX}// 限定右侧位置 else if (dx >= _pos.limitX) {dx = _pos.limitX}// 限定顶部位置 if (dy <= -_dialog.offsetTop) {dy = -_dialog.offsetTop}// 限定底部位置 else if (dy >= _pos.limitY) {dy = _pos.limitY}// 设置 位置_dialog.style.transform = `translate(${dx}px, ${dy}px)`}// 放开鼠标 事件的回调函数const mouseupCallback = () => {document.removeEventListener('mousemove', mousemoveCallback)document.removeEventListener('mouseup', mouseupCallback)}// 定义绑定事件_header.addEventListener('mousedown', (e) => {// 获取transformconst _transform = _dialog.style.getPropertyValue('transform')const _matrix = new DOMMatrix(_transform)// 计算边缘const limitX = (el.offsetWidth - _dialog.offsetWidth) / 2const limitY = el.offsetHeight - _dialog.offsetTop - _dialog.offsetHeight// 初始位置 _pos = {left: _matrix.m41,top: _matrix.m42,x: e.clientX,y: e.clientY,limitX: Math.abs(limitX),limitY: Math.abs(limitY)}// 添加事件document.addEventListener('mousemove', mousemoveCallback) // 移动事件document.addEventListener('mouseup', mouseupCallback) // 放开事件})// mousedown end}
})

        在vue中,增加el-dialog可拖拽功能,就已完成了。

版权声明:

本网仅为发布的内容提供存储空间,不对发表、转载的内容提供任何形式的保证。凡本网注明“来源:XXX网络”的作品,均转载自其它媒体,著作权归作者所有,商业转载请联系作者获得授权,非商业转载请注明出处。

我们尊重并感谢每一位作者,均已注明文章来源和作者。如因作品内容、版权或其它问题,请及时与我们联系,联系邮箱:809451989@qq.com,投稿邮箱:809451989@qq.com

热搜词