在 VueJS 项目中实现多个可拖拽的弹出框(多个可拖拽el-dialog弹出框,共用同一函数)

北城笑笑 2024-08-25 17:03:02 阅读 91

前言

在项目开发中,弹出框(Dialog)是常见的UI组件。默认情况下,弹出框的位置是固定的,但在某些场景下,我们希望用户可以自由拖动弹出框的位置,以提升用户体验。之前单个视频拖拽弹框,已经介绍过了,详细请看:

单个视频拖拽弹框

icon-default.png?t=N7T8

https://blog.csdn.net/weixin_65793170/article/details/140274477?spm=1001.2014.3001.5502

这里将详细介绍一下,如何通过创建一个 Vue mixins,实现多个可拖拽的弹出框功能。为了使多个弹窗都能独立地实现拖拽功能,你需要确保每个弹窗都有自己的拖拽逻辑。在 Vue 中,你可以通过将拖拽逻辑抽象成一个可复用的混入 mixins 来实现这一目标。这里来记录一下


一. 创建 Draggable.JS

1. 首先,我们创建一个可复用的 mixin 文件 draggable.js ,用于实现弹出框的拖拽功能,详细请看以下

<code>// src/mixins/draggable.js

export default {

// 在组件挂载后初始化拖拽功能

mounted() {

// 确保 DOM 更新完成后调用 initDraggable 方法

this.$nextTick(() => {

this.initDraggable();

});

},

methods: {

// 初始化拖拽功能

initDraggable() {

// 查找所有 el-dialog 元素

const dialogs = this.$el.querySelectorAll('.el-dialog');

// 为每个 el-dialog 元素添加拖拽功能

dialogs.forEach(dialog => {

this.makeDraggable(dialog);

});

},

// 实现拖拽功能

makeDraggable(dragDom) {

// 设置初始样式:居中并清除 margin

dragDom.style.cssText += `

margin: 0 !important;

position: absolute !important;

top: 50% !important;

left: 50% !important;

transform: translate(-50%, -50%) !important;

`;

// 设置鼠标样式为移动光标

dragDom.style.cursor = 'move';

// 获取元素的样式属性

const getStyle = (dom, attr) => getComputedStyle(dom, false)[attr];

// 鼠标按下事件处理器

const mousedownHandler = (e) => {

// 检查是否点击了不应该触发拖拽的元素

if (e.target.tagName === 'INPUT' ||

e.target.tagName === 'TEXTAREA' ||

e.target.tagName === 'SELECT' ||

e.target.tagName === 'BUTTON' ||

e.target.closest('.el-dialog__footer')) {

return;

}

// 阻止默认行为

e.preventDefault();

// 获取弹出框的位置和鼠标点击位置的偏移量

const dialogRect = dragDom.getBoundingClientRect();

const disX = e.clientX - dialogRect.left;

const disY = e.clientY - dialogRect.top;

// 获取屏幕的宽高

const screenWidth = document.documentElement.clientWidth;

const screenHeight = document.documentElement.clientHeight;

// 移除初始的居中样式

dragDom.style.left = `${dialogRect.left}px`;

dragDom.style.top = `${dialogRect.top}px`;

dragDom.style.transform = 'none';

// 鼠标移动事件处理器

const mouseMoveHandler = (e) => {

// 计算弹出框的新位置

let left = e.clientX - disX;

let top = e.clientY - disY;

// 边界检查,防止弹出框移出屏幕

left = Math.min(Math.max(left, 0), screenWidth -

dialogRect.width);

top = Math.min(Math.max(top, 0), screenHeight -

dialogRect.height);

// 设置弹出框的新位置

dragDom.style.left = `${left}px`;

dragDom.style.top = `${top}px`;

};

// 鼠标松开事件处理器

const mouseUpHandler = () => {

// 移除事件监听器

document.removeEventListener('mousemove', mouseMoveHandler);

document.removeEventListener('mouseup', mouseUpHandler);

};

// 添加事件监听器

document.addEventListener('mousemove', mouseMoveHandler);

document.addEventListener('mouseup', mouseUpHandler);

};

// 为整个弹出框添加鼠标按下事件监听器

dragDom.addEventListener('mousedown', mousedownHandler);

}

}

};};

2. 过程概述

组件挂载时初始化拖拽:当组件完成挂载并DOM更新后,mounted钩子会调用initDraggable方法,这个方法负责找到页面上的所有el-dialog元素,并为它们分别调用makeDraggable方法。

使元素可拖动makeDraggable方法接收一个DOM元素作为参数,它首先设置元素的CSS样式以使其可以被拖动。然后,它添加了一个mousedown事件监听器,以便在用户点击对话框时开始拖动过程。

处理鼠标按下事件mousedownHandler函数检查是否点击了不应该触发拖拽的元素,如输入框、文本区域、选择框、按钮或对话框的底部。如果是,则返回而不做任何操作。如果点击的是可拖动区域,它阻止了默认的鼠标行为,计算了对话框相对于鼠标点击位置的偏移量,并记录了屏幕的尺寸。

拖动过程中:当mousedown事件触发后,添加了mousemovemouseup事件监听器。mousemove事件用于实时更新对话框的位置,同时进行边界检查,以防止对话框移出屏幕。mouseup事件则用于结束拖动过程,移除相关事件监听器。

3. 注意事项

避免覆盖内部元素:在拖动过程中,需要确保对话框不会覆盖其内部的输入字段等交互元素。为此,在mousedownHandler中检查了目标元素的类型,并在必要时停止拖动。

样式覆盖:为了使对话框可拖动,使用了!important来确保样式更改优先级高于任何其他可能的CSS规则。然而,过度使用!important可能导致难以预料的样式冲突,因此在生产环境中应谨慎使用。

性能与兼容性:直接操作DOM样式可能会导致重绘和重排,影响性能。此外,不同的浏览器可能有不同的实现细节,因此需要测试以确保跨浏览器的兼容性和一致性。

事件监听器管理:在拖动结束后,必须正确地移除事件监听器,否则可能导致内存泄漏。mouseUpHandler函数负责清理这些监听器。

用户界面反馈:虽然代码中设置了鼠标光标为移动图标,但为了增强用户体验,还可以考虑在拖动过程中提供视觉反馈,例如改变背景色或边框样式。


二. 在组件中引用 Draggable.JS,并使用 Mixins 挂载

这里,在需要实现,可拖拽功能的组件中引用 draggable.js,并将其应用到 el-dialog 组件中

// 引入 draggable ,mixin 挂载

import draggable from "@/mixins/draggable.js";

export default {

mixins: [draggable], // 使用 mixin

};};


三. 使用可拖拽对话框

1. 这里,在组件模板部分,我们添加了三个 el-dialog 组件,并应用了可拖拽的 mixin。每个对话框的 title、visible、before-close 等属性根据具体需求设置

<template>

<!--弹出框1 -->

<el-dialog

title="事件1" // 对话框标题code>

:visible.sync="dialogVisible" // 控制对话框的显示状态code>

width="30%" // 对话框宽度code>

:before-close="handleClose" // 关闭对话框前的回调code>

:modal="false" // 禁用模态背景code>

:close-on-click-modal="false" // 禁用点击模态背景关闭对话code>

class="cesium_dialog"code>

>

<flvVue :Url="Your_Url1"></flvVue> // 实时视频播放组件code>

<span slot="footer" class="dialog-footer"></span> // 对话框底部空白占位code>

</el-dialog>

<!--弹出框2 -->

<el-dialog

title="事件2" // 对话框标题code>

:visible.sync="dialogVisible2" // 控制对话框的显示状态code>

width="30%" // 对话框宽度code>

:before-close="handleClose2" // 关闭对话框前的回调code>

:modal="false" // 禁用模态背景code>

:close-on-click-modal="false" // 禁用点击模态背景关闭对话code>

class="cesium_dialog"code>

>

<video src="Your_Url2" controls></video> // 视频播放组件code>

<span slot="footer" class="dialog-footer"></span> // 对话框底部空白占位code>

</el-dialog>

<!-- 弹出框3 -->

<el-dialog

title="事件3" // 对话框标题code>

:visible.sync="dialogVisible3" // 控制对话框的显示状态code>

width="30%" // 对话框宽度code>

:before-close="handleClose3" // 关闭对话框前的回调code>

:modal="false" // 禁用模态背景code>

:close-on-click-modal="false" // 禁用点击模态背景关闭对话code>

class="cesium_dialog"code>

>

<video

src="Your_Url3"code>

controls

></video> // 视频播放组件

<span slot="footer" class="dialog-footer"></span> // 对话框底部空白占位code>

</el-dialog>

</template>

<script>

import draggable from "@/mixins/draggable.js"; // 引入 draggable mixin

export default {

mixins: [draggable], // 使用 mixin

data() {

return {

dialogVisible: false, // 控制第一个弹出框的显示

dialogVisible2: false, // 控制第二个弹出框的显示

dialogVisible3: false, // 控制第三个弹出框的显示

rtsp2: 'http://your_rtsp_url' // 实时视频播放的 URL

};

},

methods: {

handleClose() {

this.dialogVisible = false; // 关闭第一个弹出框

},

handleClose2() {

this.dialogVisible2 = false; // 关闭第二个弹出框

},

handleClose3() {

this.dialogVisible3 = false; // 关闭第三个弹出框

},

DialogOpen(flag, text) {

// 根据 flag 值打开相应的弹出框

if (flag === 1) {

this.dialogVisible = true;

} else if (flag === 2) {

this.dialogVisible2 = true;

} else if (flag === 3) {

this.dialogVisible3 = true;

}

}

}

};};

</script>

2. 过程概述

弹出框配置:每个el-dialog都有其标题、宽度、可见性绑定、关闭前回调等配置。特别地,禁用了模态背景和点击模态背景关闭对话框的行为,允许用户在弹出框打开时仍能与页面其他部分交互。

视频播放:弹出框内嵌入了视频播放组件或原生的<video>标签,源地址由Your_Url1, Your_Url2, 和 Your_Url3变量指定。

混入Draggable功能:通过引入draggable.js混入,使得弹出框能够被拖动。这在组件初始化时自动应用,无需额外的配置。

数据和方法:定义了控制弹出框可见性的数据属性以及关闭弹出框的方法。DialogOpen方法可以根据传入的flag值打开特定的弹出框。

3. 注意事项

资源加载:确保视频资源URL有效且可访问,尤其是对于实时流,需要确认流服务器正常工作。

权限问题:如果视频源来自跨域,需要确保服务器支持CORS(跨源资源共享),否则视频可能无法加载。

响应式设计:虽然弹出框宽度固定为30%,但在不同屏幕尺寸上可能需要进一步优化布局,以适应不同设备。

性能考虑:同时播放多个视频可能对系统资源造成压力,特别是在资源受限的设备上。

用户交互:由于弹出框没有模态背景,应确保用户能够清晰地区分焦点,避免混淆。

代码维护性:如果弹出框具有相似的结构和功能,考虑将其抽象为一个可复用的组件,减少代码冗余。


四. 必要的 CSS

/* 确保对话框内的视频占满对话框宽度并设置高度 */

::v-deep .cesium_dialog video {

width: 100%;

height: 300px;

}

/* 禁用对话框遮罩层的点击事件 */

::v-deep .el-dialog__wrapper {

pointer-events: none !important;

}

/* 允许对话框内的元素进行交互,并设置对话框距离顶部的距离 */

::v-deep .el-dialog__wrapper .el-dialog {

pointer-events: auto !important;

margin-top: 20vh !important;

}


五. 本章总结

在以上内容中,我们探讨了如何在Vue.js应用程序中使用Element UI的el-dialog组件创建多个可拖动的对话框,同时讨论了如何优化代码结构和事件处理。

1. 主要内容概览

Draggable Mixin: 提供了一个Vue.js mixin,名为draggable.js,用于使多个el-dialog对话框可拖动。这个mixin在组件挂载后初始化拖拽功能,通过查找所有.el-dialog元素并为它们添加拖拽逻辑。拖拽功能包括设置初始样式、处理鼠标按下、移动和释放事件,以及限制对话框在屏幕内的移动范围。

Dialog Components: 展示了三个el-dialog对话框实例,分别用于显示不同的视频内容。每个对话框都配置了标题、宽度、关闭前回调、以及关闭时的行为。对话框被赋予了相同的类名cesium_dialog,这表明它们共享某些样式或功能。

Visibility Control: 为每个对话框定义了visible.sync属性,用于控制对话框的显示状态。同时,还定义了handleClose, handleClose2, 和 handleClose3方法,用于关闭各自的对话框。

2. 结论

通过使用提供的draggable mixin,我们可以轻松地使多个el-dialog组件具备拖拽功能,从而提高用户界面的交互性。同时,通过重构和优化事件处理逻辑,可以减少代码冗余,提高代码的可读性和可维护性。此外,可以直接在对话框组件上,添加外层元素实现事件委托,在其父容器上实施事件监听和逻辑处理是一种实用的替代方案。 

创作不易,感觉有用,就一键三连,感谢(●'◡'●)



声明

本文内容仅代表作者观点,或转载于其他网站,本站不以此文作为商业用途
如有涉及侵权,请联系本站进行删除
转载本站原创文章,请注明来源及作者。