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

前言

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

单个视频拖拽弹框icon-default.png?t=N7T8https://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. 过程概述

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

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

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

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

3. 注意事项

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

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

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

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

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


二. 在组件中引用 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. 过程概述

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

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

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

  4. 数据和方法:定义了控制弹出框可见性的数据属性以及关闭弹出框的方法。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. 主要内容概览

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

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

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

2. 结论

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

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

  • 13
    点赞
  • 22
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

北城笑笑

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值