vue中通过自定义指令实现一个可拖拽,缩放的弹窗

效果

在这里插入图片描述

功能描述

  • 按住头部可拖拽
  • 鼠标放到边框,可缩放
  • 多层重叠
  • 丰富的插槽,易于扩展

示例

指令代码

export const dragDialog = {inserted: function (el, { value, minWidth = 400, minHeight = 200 }) {// 让弹窗居中let dialogHeight = el.clientHeight ?? 0let dialogWidth = el.clientWidth ?? 0// 获取可视区域的宽高let windowWidth = document.documentElement.clientWidth ?? 0let windowHeight = document.documentElement.clientHeight ?? 0// 弹窗的可移动范围let leftMax = windowWidth - dialogWidthlet topMax = windowHeight - dialogHeight//还需要判断是否传入了top,left值let { top, center } = valuelet left = (windowWidth - dialogWidth) / 2if (!center) {// 没有设置centerif (top.includes('%') || top.includes('px')) {el.style.top = top} else {el.style.top = top + 'px'}el.style.left = left + 'px'} else {el.style.top = (windowHeight - dialogHeight) / 2 + 'px'el.style.left = (windowWidth - dialogWidth) / 2 + 'px'}const el_header = el.querySelector('.kl-dailog-header')// 只有点击头部才能拖拽if (!el_header) returnlet headerHeight = el_header.clientHeight - 0// 缩放相关el.onmousemove = function (e) {if(!e) return// 判断当前鼠标是否处于可以拖拽的边缘,不包含头部if (e.clientX > el.offsetLeft + el.clientWidth - 10 || el.offsetLeft + 10 > e.clientX) {el.style.cursor = 'w-resize'} else if (el.scrollTop + e.clientY >el.offsetTop + el.clientHeight - 10 - headerHeight) {el.style.cursor = 's-resize'} else {el.style.cursor = 'default'}el.onmousedown = (e) => {if(!e) return// 获取头部的宽高以及到可视区域的距离const el_header_rect = el_header.getBoundingClientRect()if (!el_header_rect) returnlet offsetTopHeader = el_header_rect.top - 0// 判断当前元素是否是可拖拽的头部元素if (headerHeight > e.pageY - offsetTopHeader) {// 是头部,拖拽相关// 获取到鼠标与被拖拽节点的相对位置let disx = e.pageX - el.offsetLeftlet disy = e.pageY - el.offsetTop// 获取弹窗的宽高let width = el.clientWidth ?? 0let height = el.clientHeight ?? 0// 设置其他弹窗的z-index 100let maxZIndex = 100document.querySelectorAll('.kl-dialog-container').forEach((item) => {let zIndex = item.style.zIndexzIndex = zIndex ? zIndex - 0 : 100if (zIndex > maxZIndex) {maxZIndex = zIndex}})el.style.zIndex = maxZIndex + 1document.onmousemove = function (e) {const el_rect = el.getBoundingClientRect()if (!el_rect) return// 获取弹窗到可视区域的距离let offsetTopEl = el_rect.top - 0let offsetLeftEl = el_rect.left - 0let left = e.pageX - disxlet top = e.pageY - disy// 对弹窗的位置进行限制if (offsetTopEl < 0 || top < 0) {top = 0}if (offsetLeftEl < 0 || left < 0) {left = 0}if (offsetTopEl + height > windowHeight || top > topMax) {top = windowHeight - height}if (offsetLeftEl + width > windowWidth || left > leftMax) {left = windowWidth - width}// 重新设置被拖拽节点的位置el.style.left = left + 'px'el.style.top = top + 'px'}document.onmouseup = function () {document.onmousemove = document.onmouseup = null}} else {const clientX = e.clientX // 鼠标点击时的X坐标const clientY = e.clientY // 鼠标点击时的Y坐标let elW = el.clientWidth // 当前元素的宽度let elH = el.clientHeight // 当前元素的高度let EloffsetLeft = el.offsetLeft // 元素距离左边的距离let EloffsetTop = el.offsetTop // 元素距离顶部的距离el.style.userSelect = 'none'let ELscrollTop = el.scrollTop // 元素滚动条距离顶部的距离// 不是头部,缩放相关document.onmousemove = function (e) {e.preventDefault() // 移动时禁用默认事件//左侧鼠标拖拽位置if (clientX > EloffsetLeft && clientX < EloffsetLeft + 10) {//往左拖拽if (clientX > e.clientX) {el.style.width = elW + (clientX - e.clientX) * 2 + 'px'el.style.left = EloffsetLeft - (clientX - e.clientX) + 'px'}//往右拖拽if (clientX < e.clientX) {if (el.clientWidth < minWidth) {} else {el.style.width = elW - (e.clientX - clientX) * 2 + 'px'el.style.left = EloffsetLeft + (e.clientX - clientX) + 'px'}}}//右侧鼠标拖拽位置if (clientX > EloffsetLeft + elW - 10 && clientX < EloffsetLeft + elW) {//往左拖拽if (clientX > e.clientX) {if (el.clientWidth < minWidth) {} else {el.style.width = elW - (clientX - e.clientX) * 2 + 'px'el.style.left = EloffsetLeft + (clientX - e.clientX) + 'px'}}//往右拖拽if (clientX < e.clientX) {el.style.width = elW + (e.clientX - clientX) * 2 + 'px'el.style.left = EloffsetLeft + (clientX - e.clientX) + 'px'}}//底部鼠标拖拽位置if (ELscrollTop + clientY > EloffsetTop + elH - 20 &&ELscrollTop + clientY < EloffsetTop + elH) {//往上拖拽if (clientY > e.clientY) {if (el.clientHeight < minHeight) {} else {el.style.height = elH - (clientY - e.clientY) * 2 + 'px'el.style.top = EloffsetTop + (clientY - e.clientY) + 'px'}}//往下拖拽if (clientY < e.clientY) {el.style.height = elH + (e.clientY - clientY) * 2 + 'px'el.style.top = EloffsetTop + (clientY - e.clientY) + 'px'}}}//拉伸结束document.onmouseup = function (e) {document.onmousemove = nulldocument.onmouseup = null}}}}},// 指令销毁unbind(el) {},
}

vue组件代码

<template><!-- 添加弹窗的动画 --><!-- <transition name="kl-dialog"> --><div class="kl-dialog" v-if="dialogVisible"><!-- 遮罩 --><div class="kl-mask" :id="klMaskId" v-if="modal" @click="close"></div><!-- 弹窗 --><!-- 这儿需要mousedown来控制顺序 --><div:id="klDialogId":class="['kl-dialog-container','resize-container',nobg ? 'kl-dialog-container-bg-no' : '',]"v-dragDialog="{ top: top, center: center }":style="{ width: width, top: top }"@mousedown="setZIndex"><!-- 弹窗头部 --><slot name="header"><!-- 必须要有这个kl-dailog-header类才能拖拽 --><div class="kl-dailog-header cc"><div class="kl-dailog-header-title">{{ title }}</div><div class="kl-dailog-header-close" @click="close"><i class="el-icon-close kl-dailog-header-close-icon"></i></div></div></slot><!-- 弹窗中间内容 --><slot> default </slot><!-- 弹窗底部 --><slot name="footer"><div class="kl-dailog-footer"><el-button @click="close">取消</el-button><el-button type="primary" @click="determine">确定</el-button></div></slot></div></div><!-- </transition> -->
</template><script>
export default {name: 'klDialog',props: {// 去除主题背景色nobg: {type: Boolean,default: false,},// 控制显示隐藏dialogVisible: {type: Boolean,default: false,},// 是否显示遮罩modal: {type: Boolean,default: true,},// 头部标题title: {type: String,default: '',},// 弹窗宽width: {type: String,default: '30%',},// 距离顶部的距离top: {type: String,default: '20%',},center: {type: Boolean,default: false,},},data() {return {klMaskId: '',klDialogId: '',}},created() {this.init()},beforeDestroy() {// console.log('beforeDestroy');},watch: {dialogVisible(val) {if (val) {this.setZIndex()}},},methods: {// 确定determine() {this.$emit('determine')},// 关闭弹窗close() {this.$emit('close')},// 给每个弹窗添加一个idinitId() {this.klMaskId = this.createId()this.klDialogId = this.createId()},// 将当前弹窗的z-index设置为最高async setZIndex() {let { klDialogId } = thisawait this.$nextTick()let els = document.querySelectorAll('.kl-dialog-container')let maxZIndex = 100els.forEach((item) => {let zIndex = item.style.zIndexzIndex = zIndex ? zIndex - 0 : 100if (zIndex > maxZIndex) {maxZIndex = zIndex}})let el = document.querySelector('#' + klDialogId)if (el) {el.style.zIndex = maxZIndex + 1}},// 初始化init() {this.initId()// 设置当前的弹窗层级最高this.setZIndex()},},
}
</script><style lang="scss" scoped>
.kl-mask {position: fixed;top: 0;left: 0;width: 100vw;height: 100vw;background-color: rgba(0, 0, 0, 0.5);z-index: 100;
}
.kl-dialog-container {position: fixed;border-radius: 2px;box-shadow: 0 1px 3px rgb(0 0 0 / 30%);box-sizing: border-box;background-color: #fff;z-index: 100;
}
.kl-dialog-container-bg-no {box-shadow: none;background: none;
}
.kl-dialog-container-center {left: 50% !important;top: 50% !important;transform: translate(-50%, -50%) !important;
}.kl-dailog-header {padding: 0 20px;height: 54px;line-height: 54px;position: relative;font-size: 18px;font-weight: 500;user-select: none;
}
.kl-dailog-header-close {position: absolute;top: 50%;right: 20px;transform: translateY(-50%);cursor: pointer;
}
.kl-dailog-header-close-icon {color: #aaa;
}
.kl-dailog-footer {padding: 0 20px;height: 70px;line-height: 70px;text-align: right;
}.cc {cursor: move;
}// 弹窗动画
.kl-dialog-enter-active,
.kl-dialog-leave-active {transition: all 0.3s;
}.kl-dialog-enter,
.kl-dialog-leave-to {opacity: 0;transform: translate(300px,300px);
}
</style>

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

如若内容造成侵权/违法违规/事实不符,请联系长河编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

Vue.js结合ASP.NET Core构建用户登录与权限验证系统

1. 环境准备2. 创建项目3. Vue配置步骤一: 安装包步骤二: 配置文件步骤三: 页面文件 4. 后台配置 在本教程中&#xff0c;我将利用Visual Studio 2022的强大集成开发环境&#xff0c;结合Vue.js前端框架和ASP.NET Core后端框架&#xff0c;从头开始创建一个具备用户登录与权限验…

C# Winform 侧边栏,切换不同页面

在项目中我们经常遇到需要在主界面上切换不同子页面的需求&#xff0c;常用做法是左侧显示子页面菜单&#xff0c;用户通过点击左侧菜单&#xff0c;实现右边子页面的展示。 实例项目实现&#xff1a; 项目左侧侧边栏实现FlowLayoutPanel使用显示不同子窗体 实例链接&#xf…

部署yum仓库

目录 安装软件包 yum 配置文件 缓存功能操作步骤 创建并配置本地仓库文件 yum相关命令 yum install __ yum repolist yum list __ yum info __ yum search __ yum whatprovides __ yum remove __ yum -y update __ yum history yum grouplist yum groupinstall…

MySQL数据库与基本操作(增删改查)

一、数据库的基本概念 数据库要学习的四个基本概念&#xff0c;主要是&#xff1a;数据、数据库系统、数据库、数据管理系统。数据&#xff08;Date&#xff09;是描述事物的记录&#xff0c;数据库系统&#xff08;DBS&#xff09;&#xff0c;数据库管理系统&#xff08;DBMS…

微服务开发与实战Day08 - Elasticsearch

一、初始Elasticsearch 高性能分布式搜索引擎 1. 认识和安装 1.1 认识 Lucene是一个Java语言的搜索引擎类库&#xff0c;是Apache公司的顶级项目&#xff0c;由DougCutting于1999年研发。官网地址&#xff1a;Apache Lucene - Welcome to Apache Lucene Lucene的优势&…

PaddleOCR学习——PP-OCR系列

相关知识前置&#xff1a; PP-LCNet PP-LCNetV3 PP-LCNetV3系列模型是PP-LCNet系列模型的延续&#xff0c;覆盖了更大的精度范围&#xff0c;能够适应不同下游任务的需要。PP-LCNetV3系列模型从多个方面进行了优化&#xff0c;提出了可学习仿射变换模块&#xff0c;对重参数…

corona渲染器与vray比哪个好?支持云渲染平台吗

​在视觉渲染技术领域&#xff0c;V-Ray和Corona都以其卓越的性能和广泛应用赢得了高度评价。这两款渲染器各有其独特的优势&#xff0c;使得在它们之间做出选择并非易事。不同的应用场景和用户需求可能会让它们各自展现出不同的优势。 一、corona渲染器跟vray怎么样 在比较V-…

制造业为什么需要ERP企业管理软件?

如今&#xff0c;传统的制造业管理方式逐渐变得力不从心~库存积压、生产效率低下、供应链混乱…想象一下&#xff0c;如果你的企业仍然依赖于手工记录订单、库存和财务数据&#xff0c;那么每当市场发生变动时&#xff0c;你就需要花费大量的时间和精力去重新调整生产计划、更新…

派克比例阀

派克比例阀 PARKER比例阀技术特征 阀芯位移反馈设计 阀之间的重复性 故障安全型中位功能 必须调整扭杆以允许&#xff08;秉圣135陈工6653负载3053&#xff09;传感比例阀介入需要时制动回路。但是&#xff0c;要小心液压回路受压时不能调整拉杆。测试应在压力缓慢上升期间…

Github2024-06-12 开源项目日报 Top10

根据Github Trendings的统计,今日(2024-06-12统计)共有10个项目上榜。根据开发语言中项目的数量,汇总情况如下: 开发语言项目数量Python项目4JavaScript项目2Lua项目1PHP项目1Blade项目1非开发语言项目1TypeScript项目1Shell项目1从零开始构建你喜爱的技术 创建周期:2156 天…

Excel 常用技巧(四)

Microsoft Excel 是微软为 Windows、macOS、Android 和 iOS 开发的电子表格软件&#xff0c;可以用来制作电子表格、完成许多复杂的数据运算&#xff0c;进行数据的分析和预测&#xff0c;并且具有强大的制作图表的功能。由于 Excel 具有十分友好的人机界面和强大的计算功能&am…

利用C#和Snap7工具模拟S7通信(包含DB地址讲解)

之前写过一篇用KepServerEx做模拟S7的通信数据&#xff0c;参考链接&#xff1a; 通过C#和KepServer完成模拟S7协议通信_c# 与kepserver-CSDN博客 但KepServerEx是收费的&#xff0c;而且模拟的DB块超过64就不行了&#xff0c;当然Snap7在本文中也是只能模拟DB1、DB2和DB3的数…

玄机平台流量特征分析-常见攻击事

前言 熟悉常见的攻击流量特征&#xff0c;我们就可以通过主机的一个流量情况来判断主机遭受了何种攻击。这里来看看玄机平台的一道题目。 步骤1.1 这里需要我们找出恶意扫描者&#xff0c;也就是黑客的ip。下载好附件之后用wiresharke打开&#xff0c;直接筛选http协议的流量…

2-9 基于matlab的传递矩阵计算轴的模态

基于matlab的传递矩阵计算轴的模态&#xff0c;包括模态频率和模态振型&#xff0c;可设置轴的结构参数。程序已调通&#xff0c;可直接运行。 2-9 传递矩阵计算轴的模态 模态频率 - 小红书 (xiaohongshu.com)

ETL可视化工具 DataX -- 简介( 一)

引言 DataX 系列文章&#xff1a; ETL可视化工具 DataX – 安装部署 ( 二)ETL可视化工具 DataX – DataX-Web安装 (三) 1.1 DataX 1.1.1 Data X概览 DataX 是阿里云DataWorks数据集成的开源版本&#xff0c;在阿里巴巴集团内被广泛使用的离线数据同步工具/平台。DataX 实现…

【Go语言精进之路】构建高效Go程序:了解string实现原理并高效使用

&#x1f525; 个人主页&#xff1a;空白诗 &#x1f525; 热门专栏&#xff1a;【Go语言精进之路】 文章目录 引言一、Go语言的字符串类型1.1 字符串的定义1.2 字符串的零值可用1.3 字符串的不可变性1.4 字符串的拼接1.5 字符串的常用方法1.6 实际使用示例 二、字符串的内部表…

多路h265监控录放开发-(1)建立head窗口并实现鼠标拖动整个窗口

头文件&#xff1a; //鼠标事件 用于拖动窗口//一下三个函数都是QWidget的可重载成员函数void mouseMoveEvent(QMouseEvent* ev) override;void mousePressEvent(QMouseEvent* ev) override;void mouseReleaseEvent(QMouseEvent* ev) override; 源文件&#xff1a; / /// 鼠标…

今年的618,似乎很平淡!

电商平台取消预售制度的第一个大促&#xff0c;快递业表现如何&#xff1f; 今年的618大促与往年有些不同&#xff0c;自4月起&#xff0c;天猫、京东、快手等主流平台相继官宣取消预售&#xff0c;打出“现货开卖”标签&#xff0c;这意味着消费者不用再被“烧脑”的优惠计算…

RoboDK试用期间提示无效或过期的许可证

问题描述 RoboDK下载下来在试用期间提示如下信息&#xff0c;不知道什么原因 临时解决方法 将C:\Users\${username}\AppData\Roaming\RoboDK该目录下的文件全部删除掉&#xff0c;便可以正常使用RoboDK应用了&#xff0c;但是等软件关闭后还是会出现上面的问题&#xff0c;…

【JAVA开发笔记】实战演练,如何用EasyExcel导出表格,并且自定义合并单元格

目录 1. 前言 2. EasyExcel简介 3. EasyExcel简单导出案例讲解 3.1 EasyExcel依赖引入 3.2 测试类创建 3.3 Excel导出实现 4. EasyExcel合并单元案例讲解 4.1 实现自定义合并策略 4.2 使用自定义合并策略 5. 总结 1. 前言 项目上&#xff0c;需将一个列表数据导出Ex…