前言
在项目开发中,弹出框(Dialog)是常见的UI组件。默认情况下,弹出框的位置是固定的,但在某些场景下,我们希望用户可以自由拖动弹出框的位置,以提升用户体验。之前单个视频拖拽弹框,已经介绍过了,详细请看:
单个视频拖拽弹框https://blog.csdn.net/weixin_65793170/article/details/140274477?spm=1001.2014.3001.5502
这里将详细介绍一下,如何通过创建一个 Vue mixins,实现多个可拖拽的弹出框功能。为了使多个弹窗都能独立地实现拖拽功能,你需要确保每个弹窗都有自己的拖拽逻辑。在 Vue 中,你可以通过将拖拽逻辑抽象成一个可复用的混入 mixins 来实现这一目标。这里来记录一下
一. 创建 Draggable.JS
1. 首先,我们创建一个可复用的 mixin 文件 draggable.js ,用于实现弹出框的拖拽功能,详细请看以下
// 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
事件触发后,添加了mousemove
和mouseup
事件监听器。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" // 对话框标题
:visible.sync="dialogVisible" // 控制对话框的显示状态
width="30%" // 对话框宽度
:before-close="handleClose" // 关闭对话框前的回调
:modal="false" // 禁用模态背景
:close-on-click-modal="false" // 禁用点击模态背景关闭对话框
class="cesium_dialog"
>
<flvVue :Url="Your_Url1"></flvVue> // 实时视频播放组件
<span slot="footer" class="dialog-footer"></span> // 对话框底部空白占位
</el-dialog>
<!--弹出框2 -->
<el-dialog
title="事件2" // 对话框标题
:visible.sync="dialogVisible2" // 控制对话框的显示状态
width="30%" // 对话框宽度
:before-close="handleClose2" // 关闭对话框前的回调
:modal="false" // 禁用模态背景
:close-on-click-modal="false" // 禁用点击模态背景关闭对话框
class="cesium_dialog"
>
<video src="Your_Url2" controls></video> // 视频播放组件
<span slot="footer" class="dialog-footer"></span> // 对话框底部空白占位
</el-dialog>
<!-- 弹出框3 -->
<el-dialog
title="事件3" // 对话框标题
:visible.sync="dialogVisible3" // 控制对话框的显示状态
width="30%" // 对话框宽度
:before-close="handleClose3" // 关闭对话框前的回调
:modal="false" // 禁用模态背景
:close-on-click-modal="false" // 禁用点击模态背景关闭对话框
class="cesium_dialog"
>
<video
src="Your_Url3"
controls
></video> // 视频播放组件
<span slot="footer" class="dialog-footer"></span> // 对话框底部空白占位
</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
组件具备拖拽功能,从而提高用户界面的交互性。同时,通过重构和优化事件处理逻辑,可以减少代码冗余,提高代码的可读性和可维护性。此外,可以直接在对话框组件上,添加外层元素实现事件委托,在其父容器上实施事件监听和逻辑处理是一种实用的替代方案。