Element-UI实现el-dialog弹框拖拽功能

news2024/11/24 4:52:39

        在实际开发中,会发现有些系统,弹框是可以在浏览器的可见区域自由拖拽的,这极大方便用户的操作。但在查看Element-UI中弹框(el-dialog)组件的文档时,发现并未实现这一功能。不过也无须担心,vue中提供了Vue.directive钩子函数,可以从底层操作DOM来实现并升级弹框拖拽的功能。

        对于Vue.directive这里就不再阐述了,上一篇中已作了相关说明,不了解此功能的朋友可以翻看一上篇了解下,地址:Element-UI - 解决el-table中图片悬浮被遮挡问题-CSDN博客,或者去官方文档了解。另外,上篇是通过自定义指令的局部定义方式实现的,此篇将通过全局模式进行定义和开发。

一、演示页面创建

        首先我们在Vue项目中,创建一个页面用于演示拖拽功能的实现。代码如下:

<template>
  <div>
    <div class="right-box">
      <el-button type="primary" size="mini" @click="dialogVisible = true">新增</el-button>
    </div>

    <el-dialog
      title="提示"
      :visible.sync="dialogVisible"
      width="30%">
      <span>这是一个弹框,升级其功能,能在浏览器可见区域自由拖拽</span>
      <span slot="footer" class="dialog-footer">
        <el-button @click="dialogVisible = false">取 消</el-button>
        <el-button type="primary" @click="dialogVisible = false">确 定</el-button>
      </span>
    </el-dialog>
  </div>
</template>

<script>
export default {
  data(){
    return {
      dialogVisible: false
    }
  },
}
</script>

<style lang="less" scoped>
.right-box{
  text-align: right;
  padding-bottom: 15px;
}
</style>

        界面效果:

二、定义v-dialogDrag

        这里在Vue项目中src/utils目录中创建dialog.js,用来定义Vue-directive('dialogDrag', {});代码如下:

import Vue from 'vue'
/**
 * 新增弹框拖拽功能
 */
Vue.directive('dialogDrag', {
  bind: (el) => {
    console.log('v-dialogDrag');
  }
})

        在main.js文件中引入dialog.js,代码如下:

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 '@/utils/dialog.js'

Vue.use(ElementUI)

Vue.config.productionTip = false

/* eslint-disable no-new */
new Vue({
  el: '#app',
  router,
  components: { App },
  template: '<App/>'
})

        页面中的el-dialog标签上添加v-dialogDrag,代码如下:

<template>
  <div>
    <div class="right-box">
      <el-button type="primary" size="mini" @click="dialogVisible = true">新增</el-button>
    </div>

    <el-dialog
      title="提示"
      v-dialogDrag
      :visible.sync="dialogVisible"
      width="30%">
      <span>这是一个弹框,升级其功能,能在浏览器可见区域自由拖拽</span>
      <span slot="footer" class="dialog-footer">
        <el-button @click="dialogVisible = false">取 消</el-button>
        <el-button type="primary" @click="dialogVisible = false">确 定</el-button>
      </span>
    </el-dialog>
  </div>
</template>

        在定义v-dialogDrag时,在钩子函数中添加了控制台输出,bind是一次性的初始化设置,所以在组件绑定后只调用一次。此时查看控制台可以看到输出内容,如下图:

三、DOM操作

        在bind钩子函数中,将接收到的参数el输出查看可以发现,el为弹框的DOM节点。此时,我们就可以通过el节点获取到弹框标题.el-dialog__header部分,并对其进行事件监听,实现弹框拖拽功能。

3.1 修改鼠标样式

        修改弹框标题区域鼠标样式,当鼠标放到标题区域时呈现出移动图标,代码如下:

Vue.directive('dialogDrag', {
  bind: (el) => {
    // 获取弹框标题区域DOM节点
    const headerDOM = el.querySelector('.el-dialog__header');
    // 修改鼠标图标样式
    headerDOM.style.cursor = "move";
    // 禁止拖拽时选中标题中文本内容
    headerDOM.style.userSelect = "none";

    console.log('v-dialogDrag', el);
  }
})

3.2 添加监听事件

        对于拖拽功能会使用到onmousedown、onmousemove、onmouseup三个事件,分别对应鼠标按下、鼠标移动、鼠标松开三个动作。代码如下:

Vue.directive('dialogDrag', {
  bind: (el) => {
    // 获取弹框标题区域DOM节点
    const headerDOM = el.querySelector('.el-dialog__header');
    // 修改鼠标图标样式
    headerDOM.style.cursor = "move";
    // 禁止拖拽时选中标题中文本内容
    headerDOM.style.userSelect = "none";
    // 获取弹框区域的DOM节点
    const dialogDOM = el.querySelector('.el-dialog');

    let isDown = false;   //是否按下
    // 监听鼠标按下事件
    headerDOM.onmousedown = e => {
      isDown = true;
      console.log('mouse down', e);
    }
    // 监听鼠标移动事件
    headerDOM.onmousemove = e => {
      // 不按下的时候,执行移动操作
      if(isDown){
        console.log('mouse move', e);
      }
    }
    // 监听鼠标松开事件
    headerDOM.onmouseup = e => {
      console.log('mouse up', e);
      isDown = false;     //
    }
  }
})

        使用鼠标在住标题区域,进行按下、移动、松开等操作,查看控制台输出结果如下:

3.3 计算并重置弹框位置

        现在我们则可以通过获取相应的参数数据,对弹框进行位置计算,使其根据鼠标移动的位置进行拖拽了。代码如下:

Vue.directive('dialogDrag', {
  bind: (el) => {
    // 获取弹框标题区域DOM节点
    const headerDOM = el.querySelector('.el-dialog__header');
    // 修改鼠标图标样式
    headerDOM.style.cursor = "move";
    // 禁止拖拽时选中标题中文本内容
    headerDOM.style.userSelect = "none";
    // 获取弹框区域的DOM节点
    const dialogDOM = el.querySelector('.el-dialog');

    let isDown = false,         // 是否按下
        // 鼠标按下时坐标位置
        clientX = 0,
        clientY = 0,
        // 按下时弹框位置
        dialogLeft = 0,
        dialogTop = 0;
    // 监听鼠标按下事件
    headerDOM.onmousedown = e => {
      isDown = true;
      // 获取当前鼠标按钮位置坐标
      clientX = e.clientX;
      clientY = e.clientY;
      // 获取弹框位置(默认情况弹框样式left和top可能不存在,当为NaN时初始化为0)
      dialogLeft = isNaN(parseFloat(dialogDOM.style.left))?0:parseFloat(dialogDOM.style.left);
      dialogTop = isNaN(parseFloat(dialogDOM.style.top))?0:parseFloat(dialogDOM.style.top);
    }
    // 监听鼠标移动事件
    headerDOM.onmousemove = e => {
      // 不按下的时候,执行移动操作
      if(isDown){
        // 获取当前移动到的位置坐标,与按下位置坐标进行计算,获取移动距离
        const distX = e.clientX - clientX;
        const distY = e.clientY - clientY;
        // 修改弹框位置
        dialogDOM.style.left = (dialogLeft + distX) + "px";
        dialogDOM.style.top = (dialogTop + distY) + "px";
      }
    }
    headerDOM.onmouseup = () => isDown = false;         // 监听标题区域鼠标是否松开
    dialogDOM.onmouseleave = () => isDown = false;      // 监听鼠标是否移出弹框区域
  }
})

        以上对计算方法都代码中进行说明了,大家可根据解释进行实操并体会其中原理。以上功能实现后,弹框则可以通过按住标题区域进行拖放了。如下图:

3.4 限定区域

        当弹框拖拽功能实现后,会发现其有时会超出界面范围,并影响拖拽效果。对于这块,我们可以通过计算,来限定其可移动范围。

        之前我们学习过getBoundingClientRect()可获取DOM元素的边界信息,当left或top小于0时,则弹框已超出了顶部或者左侧可见区域,判断是否超过右侧或底部可见区域,则需要通过width和height,以及window.innerWidth和window.innerHeight结合计算得出结论。

        通过上述的分析,我们来定义一个函数用于判断当前时否可执行移动操作。代码如下:

// 定义函数判断当前是否在可见范围内
function boundingRange(){
  const bounding = dialogDOM.getBoundingClientRect();
  return {
	top: bounding.top >= 0,       // 表示顶部在可见范围
	left: bounding.left >= 0,     // 表示左侧在可见范围
	right: bounding.left <= window.innerWidth - bounding.width,   // 表示右侧在指定范围
	bottom: bounding.top < window.innerHeight - bounding.height   // 表示底部在指定范围
  }
}

        当添加到移动事件中并获取边界范围,通过获取的结果来判断是否为可操作状态。true为可操作,false为不可操作。如下图:

        此时,我们要在mousemove事件中对计算方式修改一下,最终完整代码如下:

Vue.directive('dialogDrag', {
  bind: (el) => {
    // 获取弹框标题区域DOM节点
    const headerDOM = el.querySelector('.el-dialog__header');
    // 修改鼠标图标样式
    headerDOM.style.cursor = "move";
    // 禁止拖拽时选中标题中文本内容
    headerDOM.style.userSelect = "none";
    // 获取弹框区域的DOM节点
    const dialogDOM = el.querySelector('.el-dialog');

    let isDown = false,         // 是否按下
        // 鼠标按下时坐标位置
        clientX = 0,
        clientY = 0,
        // 按下时弹框位置
        dialogLeft = 0,
        dialogTop = 0;
    // 定义函数判断当前是否在可见范围内
    function boundingRange(){
      const bounding = dialogDOM.getBoundingClientRect();
      return {
        top: bounding.top >= 0,       // 表示顶部在可见范围
        left: bounding.left >= 0,     // 表示左侧在可见范围
        right: bounding.left < window.innerWidth - bounding.width,   // 表示右侧在指定范围
        bottom: bounding.top < window.innerHeight - bounding.height   // 表示底部在指定范围
      }
    }
    // 监听鼠标按下事件
    headerDOM.onmousedown = e => {
      isDown = true;
      // 获取当前鼠标按钮位置坐标
      clientX = e.clientX;
      clientY = e.clientY;
      // 获取弹框位置(默认情况弹框样式left和top可能不存在,当为NaN时初始化为0)
      dialogLeft = isNaN(parseFloat(dialogDOM.style.left))?0:parseFloat(dialogDOM.style.left);
      dialogTop = isNaN(parseFloat(dialogDOM.style.top))?0:parseFloat(dialogDOM.style.top);
    }
    // 监听鼠标移动事件
    headerDOM.onmousemove = e => {
      // 不按下的时候,执行移动操作
      if(isDown){
        // 获取DOM边界范围
        const range = boundingRange();
        // 获取当前移动到的位置坐标,与按下位置坐标进行计算,获取移动距离
        const distX = e.clientX - clientX;          // distX小于0为向左,大于0为向右
        const distY = e.clientY - clientY;          // distY小于0为向上,大于0为向下
        // 判断左侧或右侧是否可移动
        if((range.left && distX < 0) || (range.right && distX >= 0)) dialogDOM.style.left = (dialogLeft + distX) + "px";
        // 判断顶部或者底部是否可移动
        if((range.top && distY < 0) || (range.bottom && distY >= 0)) dialogDOM.style.top = (dialogTop + distY) + "px";
      }
    }
    headerDOM.onmouseup = () => isDown = false;         // 监听标题区域鼠标是否松开
    dialogDOM.onmouseleave = () => isDown = false;      // 监听鼠标是否移出弹框区域
  }
})

        目前弹框的拖拽功能就已完成了,希望对大家有所帮助。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1846408.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

GaussDB关键技术原理:高性能(一)

引言 对数据库性能进行优化是令人激动的&#xff0c;无论是对其进行性能需求分析、性能需求设计、性能问题定个位都是富于变化又充满挑战的工作&#xff0c;本章围绕“数据库性能”进行全面系统化的介绍&#xff0c;首先从数据库在现代软件栈中所处的位置出发&#xff0c;介绍…

车载模块负载基础认识

车载模块负载是指车辆上的各种电子设备和系统&#xff0c;如导航系统、音响系统、空调系统、安全气囊等。这些设备和系统在车辆运行过程中需要消耗一定的电能&#xff0c;以保证其正常工作。车载模块负载的基础认识主要包括以下几个方面&#xff1a; 1. 负载类型&#xff1a;车…

计算机毕业设计Python+Spark音乐推荐系统 音乐数据分析 音乐可视化 音乐爬虫 音乐大数据 大数据毕业设计 大数据毕设

2023届本科生毕业论文&#xff08;设计&#xff09;开题报告 知识图谱音乐推荐系统 学 院&#xff1a; XXX 专 业&#xff1a; XXX 年 级 班 级&#xff1a; XXX 学 生 姓 名&#xff1a; XXX 指 导 教 师&#xff1a; XXX 协助指导教师&#xff1a; …

收藏丨世界地形图(超高清)

原文链接https://mp.weixin.qq.com/s?__bizMzUyNzczMTI4Mg&mid2247668146&idx2&snbebd2f4921994ab05ed47efdebe8e706&chksmfa770e8fcd008799bf1d1cabd62edca7f60a5c7a1ef9c0bacf3bdc102bccf0f12d54d6c07c49&token1405091246&langzh_CN&scene21#we…

[信号与系统]模拟域中的一阶低通滤波器和二阶滤波器

前言 不是学电子出身的&#xff0c;这里很多东西是问了朋友… 模拟域中的一阶低通滤波器传递函数 模拟域中的一阶低通滤波器的传递函数可以表示为&#xff1a; H ( s ) 1 s ω c H(s) \frac{1}{s \omega_c} H(s)sωc​1​ 这是因为一阶低通滤波器的设计目标是允许低频信…

Java 中 String 类

目录 1 常用方法 1.1 字符串构造 1.2 字符串包含的成员 1.3 String 对象的比较 1.4 字符串查找 1.5 转化 1.5.1 数值和字符串转化 1.5.2 大小写转化 1.5.3 字符串转数组 1.5.4 格式化 1.6 字符串替换 1.7 字符串拆分 1.8 字符串截取 1.9 其他操作方法 1.10 字符…

智慧办公新篇章:可视化技术引领园区管理革命

随着科技的飞速发展&#xff0c;办公方式也在经历着前所未有的变革。在这个信息爆炸的时代&#xff0c;如何高效、智能地管理办公空间&#xff0c;成为了每个企业和园区管理者面临的重要课题。 智慧办公园区作为未来办公的新趋势&#xff0c;以其高效、便捷、智能的特点&#x…

面向对象修炼手册(一)(类与对象)(Java宝典)

&#x1f308; 个人主页&#xff1a;十二月的猫-CSDN博客 &#x1f525; 系列专栏&#xff1a; &#x1f3c0;面向对象修炼手册 &#x1f4aa;&#x1f3fb; 十二月的寒冬阻挡不了春天的脚步&#xff0c;十二点的黑夜遮蔽不住黎明的曙光 目录 思想 代理和团体 类 1 基本概…

设施布置之车间布局优化SLP分析

一 物流分析&#xff08;Flow Analysis&#xff09; 的基本方法 1、当物料移动是工艺过程的主要部分时&#xff0c;物流分析就是工厂布置设计的核心工作&#xff0c;也是物料搬运分析的开始。 2、零部件物流是该部件在工厂内移动时所走过的路线&#xff0c; 物流分析不仅要考虑…

鸿蒙开发通信与连接:【@ohos.wifi (WLAN)】

WLAN 说明&#xff1a; 本模块首批接口从API version 6开始支持。后续版本的新增接口&#xff0c;采用上角标单独标记接口的起始版本。 导入模块 import wifi from ohos.wifi;wifi.isWifiActive isWifiActive(): boolean 查询WLAN是否已激活。 需要权限&#xff1a; ohos.p…

java基于ssm+jsp KTV点歌系统

1管理员功能模块 管理员登录&#xff0c;通过填写注册时输入的用户名、密码进行登录&#xff0c;如图1所示。 图1管理员登录界面图 管理员登录进入KTV点歌系统可以查看个人中心、用户管理、歌曲库管理、歌曲类型管理、点歌信息管理等信息。 修改密码&#xff0c;在修改密码页…

【数据分享】《中国改革年鉴》1989-2022

最近老有同学过来询问《中国经济体制改革年鉴》、《中国改革年鉴》这两本数据的关系以及怎么获取这两本本数据。今天就在这里给大家分享一下这三本数据的具体情况。 《中国改革年鉴》由国家发展和改革委员会主管,中国经济体制改革研究会主办,中国经济体制改革杂志社编辑出版,是…

iOS开发工具-网络封包分析工具Charles

一、Charles简介 Charles 是在 Mac 下常用的网络封包截取工具&#xff0c;在做 移动开发时&#xff0c;我们为了调试与服务器端的网络通讯协议&#xff0c;常常需要截取网络封包来分析。 Charles 通过将自己设置成系统的网络访问代理服务器&#xff0c;使得所有的网络访问请求…

Lighthouse浮游菌采样器AC100H及操作使用介绍-中邦兴业

Lighthouse浮游菌采样器AC100H介绍 Lighthouse浮游菌采样器AC100是一款高性能的便携式浮游菌采样器&#xff0c;专为洁净室和无菌环境设计。它基于先进的光学技术和安德森撞击原理&#xff0c;实现了对微小浮游菌的高效采集。采样器内置了HEPA高效过滤器&#xff0c;能够过滤掉…

泽众云真机-平台即将升级支持华为机型HarmonyOS NEXT系统

具小编了解&#xff0c;泽众云真机即将升级支持华为机型HarmonyOS NEXT系统。有些人可能对HarmonyOS NEXT系统了解不多。 之前我们有个银行项目&#xff0c;客户要求测试华为HarmonyOS NEXT系统环境&#xff0c;当时我们云真机尚未有该系统的机型&#xff0c;然后技术人员向华为…

React的服务器端渲染(SSR)和客户端渲染(CSR)有什么区别?

React的服务器端渲染&#xff08;SSR&#xff09;和客户端渲染&#xff08;CSR&#xff09;是两种不同的页面渲染方式&#xff0c;它们各自有不同的特点和适用场景&#xff1a; 服务器端渲染&#xff08;SSR&#xff09; 页面渲染: 页面在服务器上生成&#xff0c;然后将完整的…

如何最简单的方式使用nodejs中的http-server发布轻量级的html网页

1、查看nodejs是否安装。 node 2、设置环境路径。 3、使用npm install http-server -g安装http-server >npm install http-server -g 5、启动http-server服务,查看是否正确安装。 http-server 6、查看是否能够正常运行。 5、创建文件夹&#xff0c;复制html、css、js、in…

使用Rsbuild构建基于Vue3+Vant4开发h5应用

一、介绍 1.1 Vant介绍 Vant 是一个轻量、可定制的移动端组件库&#xff0c;于 2017 年开源。 目前 Vant 官方提供了 Vue 2 版本、Vue 3 版本和微信小程序版本&#xff0c;并由社区团队维护 React 版本和支付宝小程序版本。 使用文档&#xff1a;快速上手 - Vant 4 (vant-u…

35、正则表达式

一、正则表达式命令 正则表达式&#xff1a;匹配的是文本内容&#xff0c;linux的文本三剑客都是针对文本内容。 ​ grep 过滤文本内容 ​ sed 针对文本内容进行增删改查 ​ awk 按行取列 文本三剑客----都是按照行进行匹配。 1.1、grep筛选&#xff1a; grep的作用就是…

Swift Combine — zip和combineLatest的理解与使用

Publisher 上还有一些其他的操作&#xff0c;比如 zip 和 combineLatest&#xff0c;能让我们在时序上对控制多个 Publisher 的结果进行类似 and 和 or 的合并&#xff0c;它们在构建复杂 Publisher 逻辑时也十分有用。 zip Publisher 中的 zip 和 Sequence 的 zip 相类似&am…