当前位置: 首页 > news >正文

Vue实现可拖拽边界布局

Vue实现可拖拽边界布局

在前端开发中,有时需要实现一种可拖拽边界的布局,通过拖动分隔线来调整不同区域大小。例如,下图是一个典型的可拖拽边界布局,它由左右两个区域组成,左边是一个树形菜单,右边是一个上下分割的内容区域。用户可以通过拖动水平和垂直的分隔线来改变左右区域和上下区域的宽度和高度。
在这里插入图片描述

本文用Vue来实现这种可拖拽边界布局,只需要用到Vue的基本特性,如数据绑定、事件处理、样式绑定等(额外的el-tree基于elementui可不加)。主要涉及到以下几个方面:

  • 布局结构:使用flex布局来实现容器和子元素的分配,使用style绑定来动态调整区域的大小,使用cursor属性来改变鼠标的形状。
  • 数据定义:使用data选项来定义不同区域的宽度和高度,以及是否正在拖动分隔线,以及拖动开始时的鼠标位置和区域大小。
  • 事件处理:使用methods选项来定义开始拖动、拖动中和结束拖动的函数,使用draggingH和draggingV来判断拖动的方向,使用startX和startY来记录拖动的起点,使用delta来计算拖动的距离,使用leftWidth、rightWidth、topHeight和bottomHeight来更新区域的大小。
  • 事件绑定:使用v-on指令来绑定分隔线的mousedown事件,表示用户开始拖动分隔线,给document绑定mousemove事件,表示用户正在拖动分隔线,给document绑定mouseup事件,表示用户结束拖动分隔线。

布局结构

首先定义布局的结构,这里使用flex布局来实现。布局由一个容器div和四个子div组成,分别是左边区域、右边区域、水平分隔线和垂直分隔线。容器div的display属性设置为flex,表示它是一个弹性盒子,它的子元素可以按照一定的比例分配空间。左边区域和右边区域的flex-direction属性设置为column,表示它们是一个垂直方向的弹性盒子,它们的子元素可以按照一定的比例分配高度。右边区域又由上下两个子div组成,分别是上面区域和下面区域。水平分隔线和垂直分隔线的宽度和高度分别设置为10px,表示它们是分隔线的宽度。水平分隔线的cursor属性设置为col-resize,表示当鼠标移动到分隔线上时,鼠标的形状会变成一个水平方向的双箭头,表示可以拖动分隔线。垂直分隔线的cursor属性设置为row-resize,表示当鼠标移动到分隔线上时,鼠标的形状会变成一个垂直方向的双箭头,表示可以拖动分隔线。我们还可以给分隔线添加一些样式,如背景色、边框等,以增加视觉效果。以下是布局结构的代码:

<template><div id="app"><div class="container"><div class="left" :style="{ width: leftWidth + 'px' }"><el-tree class="tree" :data="treeData" :props="defaultProps" node-key="id"></el-tree></div><div class="divider-h" @mousedown="startDragH"><span>||</span></div><div class="right" :style="{ width: rightWidth + 'px' }"><div class="top" :style="{ height: topHeight + 'px' }"><p>这是右边上面的区域</p></div><div class="divider-v" @mousedown="startDragV"><!-- <span>==</span> --></div><div class="bottom" :style="{ height: bottomHeight + 'px' }"><p>这是右边下面的区域</p></div></div></div></div>
</template>

数据定义

接下来定义一些数据,用来表示不同区域的宽度和高度,以及是否正在拖动分隔线,以及拖动开始时的鼠标位置和区域大小。我们可以在Vue实例的data选项中定义这些数据,如下所示:

export default {name: "App",data() {return {containerWidth: 800, // 容器的宽度containerHeight: 600, // 容器的高度leftWidth: 400, // 左边区域的宽度rightWidth: 400, // 右边区域的宽度topHeight: 300, // 右边上面区域的高度bottomHeight: 300, // 右边下面区域的高度draggingH: false, // 是否正在水平拖动draggingV: false, // 是否正在垂直拖动startX: 0, // 水平拖动开始时的鼠标位置startY: 0, // 垂直拖动开始时的鼠标位置startLeftWidth: 0, // 水平拖动开始时的左边区域宽度startRightWidth: 0,startTopHeight: 0, // 垂直拖动开始时的右边上面区域高度startBottomHeight: 0,};},
};

事件处理

然后需要定义一些事件处理函数,用来实现拖动分隔线的逻辑。监听分隔线的mousedown事件,表示用户开始拖动分隔线,以及document的mousemove事件,表示用户正在拖动分隔线,以及document的mouseup事件,表示用户结束拖动分隔线。我们可以在Vue实例的methods选项中定义这些事件处理函数,如下所示:

methods: {// 开始水平拖动startDragH(e) {this.draggingH = true;this.startX = e.clientX;this.startLeftWidth = this.leftWidth;this.startRightWidth = this.rightWidth;},// 开始垂直拖动startDragV(e) {this.draggingV = true;this.startY = e.clientY;this.startTopHeight = this.topHeight;this.startBottomHeight = this.bottomHeight;},// 拖动中onDrag(e) {if (this.draggingH) {let delta = e.clientX - this.startX;// 更新左右区域的宽度this.leftWidth = this.startLeftWidth + delta;this.rightWidth = this.startRightWidth - delta;}if (this.draggingV) {let delta = e.clientY - this.startY;// 更新上下区域的高度this.topHeight = this.startTopHeight + delta;this.bottomHeight = this.startBottomHeight - delta;}},// 结束拖动endDrag() {this.draggingH = false;this.draggingV = false;},},

在开始水平拖动和开始垂直拖动的函数中,设置draggingH和draggingV为true,表示正在拖动分隔线,同时记录下鼠标的位置和区域的大小,作为拖动的起点。在拖动中的函数中,我们需要根据鼠标的位置和拖动的起点计算出拖动的距离,然后根据拖动的距离更新左右区域和上下区域的宽度和高度。在结束拖动的函数中,我们需要设置draggingH和draggingV为false,表示停止拖动分隔线。

事件绑定

最后给水平分隔线和垂直分隔线绑定mousedown事件,表示用户开始拖动分隔线,给document绑定mousemove事件

  mounted() {// 监听鼠标移动和松开事件document.addEventListener("mousemove", this.onDrag);document.addEventListener("mouseup", this.endDrag);},beforeDestroy() {// 移除事件监听document.removeEventListener("mousemove", this.onDrag);document.removeEventListener("mouseup", this.endDrag);},
};

样式定义

最后,我们需要给布局的元素添加一些样式,以增加辨识度。我们可以在Vue实例的style选项中定义这些样式

完整代码

以下是完整的代码,你可以复制到编辑器中运行

<template><div id="app"><div class="container"><div class="left" :style="{ width: leftWidth + 'px' }"><el-tree class="tree" :data="treeData" :props="defaultProps" node-key="id"></el-tree></div><div class="divider-h" @mousedown="startDragH"><span>||</span></div><div class="right" :style="{ width: rightWidth + 'px' }"><div class="top" :style="{ height: topHeight + 'px' }"><p>这是右边上面的区域</p></div><div class="divider-v" @mousedown="startDragV"><!-- <span>==</span> --></div><div class="bottom" :style="{ height: bottomHeight + 'px' }"><p>这是右边下面的区域</p></div></div></div></div>
</template><script>
export default {name: "App",data() {return {containerWidth: 800, // 容器的宽度containerHeight: 600, // 容器的高度leftWidth: 400, // 左边区域的宽度rightWidth: 400, // 右边区域的宽度topHeight: 300, // 右边上面区域的高度bottomHeight: 300, // 右边下面区域的高度draggingH: false, // 是否正在水平拖动draggingV: false, // 是否正在垂直拖动startX: 0, // 水平拖动开始时的鼠标位置startY: 0, // 垂直拖动开始时的鼠标位置startLeftWidth: 0, // 水平拖动开始时的左边区域宽度startRightWidth: 0,startTopHeight: 0, // 垂直拖动开始时的右边上面区域高度startBottomHeight: 0,treeData: [{id: 1,label: "一级 1",children: [{id: 4,label: "二级 1-1",children: [{id: 9,label: "三级 1-1-1",},{id: 10,label: "三级 1-1-2",},],},],},{id: 2,label: "一级 2",children: [{id: 5,label: "二级 2-1",},{id: 6,label: "二级 2-2",},],},{id: 3,label: "一级 3",children: [{id: 7,label: "二级 3-1",},{id: 8,label: "二级 3-2",},],},],defaultProps: {children: "children",label: "label",},};},methods: {// 开始水平拖动startDragH(e) {this.draggingH = true;this.startX = e.clientX;this.startLeftWidth = this.leftWidth;this.startRightWidth = this.rightWidth;},// 开始垂直拖动startDragV(e) {this.draggingV = true;this.startY = e.clientY;this.startTopHeight = this.topHeight;this.startBottomHeight = this.bottomHeight;},// 拖动中onDrag(e) {if (this.draggingH) {// 计算水平拖动的距离let delta = e.clientX - this.startX;// 更新左右区域的宽度this.leftWidth = this.startLeftWidth + delta;this.rightWidth = this.startRightWidth - delta;}if (this.draggingV) {// 计算垂直拖动的距离let delta = e.clientY - this.startY;// 更新上下区域的高度this.topHeight = this.startTopHeight + delta;this.bottomHeight = this.startBottomHeight - delta;}},// 结束拖动endDrag() {this.draggingH = false;this.draggingV = false;},onresize() {this.leftWidth = window.innerWidth * 0.3 - 5this.rightWidth = window.innerWidth * 0.7 - 5this.topHeight = window.innerHeight * 0.5 - 5this.bottomHeight = window.innerHeight * 0.5 - 5console.log(window.screen);}},mounted() {// 监听鼠标移动和松开事件document.addEventListener("mousemove", this.onDrag);document.addEventListener("mouseup", this.endDrag);window.addEventListener("resize", this.onresize);this.leftWidth = window.innerWidth * 0.2 - 5this.rightWidth = window.innerWidth * 0.8 - 5this.topHeight = window.innerHeight * 0.5 - 5this.bottomHeight = window.innerHeight * 0.5 - 5// },beforeDestroy() {// 移除事件监听document.removeEventListener("mousemove", this.onDrag);document.removeEventListener("mouseup", this.endDrag);},
};
</script><style>
html,
body {width: 100%;height: 100%;margin: 0;padding: 0;
}.container {display: flex;width: 100%;height: 100%;/* border: 1px solid black; */
}.left {display: flex;flex-direction: column;background-color: lightblue;height: 100%;width: 30%;
}.right {display: flex;flex-direction: column;background-color: lightgreen;height: 100%;width: 70%;}.top {background-color: blueviolet;
}.bottom {background-color: bisque;
}.divider-h {width: 10px;cursor: col-resize;
}.divider-h span {display: block;margin-top: 290px;
}.divider-v {height: 10px;cursor: row-resize;background-color: aliceblue;
}.divider-v span {display: block;margin-left: 190px;
}.tree {flex: 1;overflow: auto;cursor: pointer;
}</style>

相关文章:

Vue实现可拖拽边界布局

Vue实现可拖拽边界布局 在前端开发中&#xff0c;有时需要实现一种可拖拽边界的布局&#xff0c;通过拖动分隔线来调整不同区域大小。例如&#xff0c;下图是一个典型的可拖拽边界布局&#xff0c;它由左右两个区域组成&#xff0c;左边是一个树形菜单&#xff0c;右边是一个上…...

Day41力扣打卡

打卡记录 第 N 位数字&#xff08;找规律&#xff09; 链接 class Solution:def findNthDigit(self, n: int) -> int:count, digit, start 9, 1, 1while n > count:n - countdigit 1start * 10count start * 9 * digitnum start (n - 1) // digitreturn int(str(n…...

SpringBoot项目发送邮件

&#x1f4d1;前言 本文主要是【SpringBoot】——SpringBoot项目发送邮件的文章&#xff0c;如果有什么需要改进的地方还请大佬指出⛺️ &#x1f3ac;作者简介&#xff1a;大家好&#xff0c;我是听风与他&#x1f947; ☁️博客首页&#xff1a;CSDN主页听风与他 &#x1f3…...

Mac单独修改应用语言

方法1: 方法2: defaults write com.microsoft.Excel AppleLanguages ("zh-cn") defaults write com.microsoft.Word AppleLanguages ("zh-cn")参考&#xff1a;https://www.zhihu.com/question/24976020...

Unity 通过代码控制Texture进行缩放

在实际应用开发中&#xff0c;有时候需要通过代码对Texture进行缩放。 有两个方法&#xff0c;一个是通过控制宽高进行缩放&#xff0c;另一个是通过比例值进行等比例缩放。 1、控制宽高的方法&#xff1a; /// <summary>/// 纹理缩放方法一&#xff0c;指定宽高/// &…...

C语言:输入3个整数,按由小到大的顺序输出(指针)

分析&#xff1a; 定义三个整型变量 a、b、c&#xff0c;和三个指向整型变量的指针变量 i、j、k。然后使用 scanf 函数从标准输入&#xff08;键盘&#xff09;中读取输入的三个整数&#xff0c;并将它们存储到 a、b、c 中。注意&#xff0c;使用 &a、&b、&c 进行赋…...

C# 模拟鼠标操作工具类

写在前面 用WinForm做RPA项目时经常需要模拟鼠标操作&#xff0c;通过调用Windows Api 可以实现控制鼠标的移动、点击以及滚轮滚动&#xff0c;做到跟人工一样的操作。 代码实现 public static class MouseKeyController{[DllImport("user32")]private static exte…...

content_script.js、background.js和popup.js之间的通讯

1. content_script.js 和 background.js 之间的通信&#xff1a; 使用 chrome.runtime.sendMessage 发送消息&#xff0c;然后在 background.js 中使用 chrome.runtime.onMessage 监听消息并作出相应处理。使用 chrome.extension.sendMessage 发送消息&#xff0c;然后在 back…...

python的requests请求参数带files

踩坑接口请求参数含文件 requests接口请求既有file&#xff0c;也有json。划重点params requests 官网地址 https://requests.readthedocs.io/en/stable/user/quickstart/#post-a-multipart-encoded-file 接口请求既有file&#xff0c;也有json。划重点params import reques…...

Elk:filebeat 日志收集工具和logstash

Elk:filebeat 日志收集工具和logstash Filebeat是一个轻量级的日志手机工具,所使用的系统资源比logstash部署和启动时使用的资源要小得多 Filebeat可以在非java环境使用&#xff0c;他可以代理logstash在非java环境上收集日志 缺点 Filebeat无法实现数据的过滤,一般是结合l…...

[设计模式] 常见的设计模式

文章目录 设计模式的 6 大设计原则设计模式的三大分类常见的设计模式有哪几种1. 单例模式&#xff1a;保证一个类仅有一个实例&#xff0c;并提供一个访问它的全局访问点。&#xff08;连接池&#xff09;1. 饿汉式2. 懒汉式3. 双重检测 2. 工厂模式3. 观察者模式● 推模型● 拉…...

报错解决:You may need an additional loader to handle the result of these loaders.

报错信息如下 vue 项目 Module parse failed: Unexpected token (1:9) File was processed with these loaders:* ./node_modules/vue/cli-plugin-babel/node_modules/cache-loader/dist/cjs.js* ./node_modules/babel-loader/lib/index.js* ./node_modules/eslint-loader/in…...

配置自动化部署Jenkins和Gitea

配置自动化部署 这里使用的是JenkinsGitea 如果不知道怎么安装Jenkins和Gitea可以参考下面文章 https://blog.csdn.net/weixin_46533577/article/details/134644144 我的另一篇文章 介绍 前端 先说下自己的情况&#xff0c;因为自己服务器原因&#xff0c;使用的服务器内…...

VSCODE+QEMU+WSL调试RISCV代码(SBI、kernel)

前言 最近在对RISC-V架构比较感兴趣&#xff0c;正好手头有《RISC-V体系结构编程与实践》的书籍&#xff0c;就打算跟随笨叔将这块的知识学习起来&#xff0c;最开始当然是需要搭建一个基础的实验平台&#xff0c;本来笨叔是贴心的提供了VMare的环境&#xff0c;奈何天生叛逆的…...

二叉树(判断是否为对称二叉树)

题目&#xff08;力扣&#xff09;&#xff1a; 观察题目&#xff0c;只需判断该二叉树是否对称。 判断二叉树是否对称&#xff0c;就可以换位去判断该二叉树的左子树和右子树是否对称。 这时就可以写一个辅助函数来方便判断。 该函数是判断两颗树是否镜像对称&#xff0c;这…...

STM32开发学习(地址映射)

LED灯代码&#xff1a; #define PERIPH_BASE ((unsigned int)0x40000000)#define AHB1PERIPH_BASE (PERIPH_BASE 0x00020000)#define GPIOF_BASE (AHB1PERIPH_BASE 0x1400)#define GPIOF_MODER *(unsigned int*)(GPIOF_BASE0x00) #define GPIOF_BSRR *(uns…...

证明E(X+Y) =E(X) + E(Y)

E(XY) E(X) E(Y)的成立是不需要X和Y相互独立的&#xff01;&#xff01;&#xff01; 离散型随机变量 E ( X Y ) ∑ i 1 n ∑ j 1 m ( x i y j ) P { X x i , Y y j } ∑ i 1 n ∑ j 1 m x i P { X x i , Y y j } ∑ i 1 n ∑ j 1 m y j P { X x i , Y y j …...

ClickHouse入门手册1.0

1、数据类型 1.1 整数类型&#xff1a; ClickHouse中整型数据均为固定长度(可以设置长度参数&#xff0c;但是会被忽略)&#xff0c;整型包括有符号整型和无符号整型。 有符号整型&#xff1a;Int8,Int16,Int32,Int64,Int128,Int256 无符号整型&#xff1a;UInt8,UInt16,UI…...

10个火爆的设计素材网站推荐

所谓聪明的女人没有米饭很难做饭&#xff0c;设计师也是如此。如何找到优秀的设计材料是每个设计师的痛点&#xff0c;国内材料网站收费&#xff0c;但也限制使用范围和期限&#xff0c;大多数外国设计网站不能打开或需要特殊互联网使用&#xff0c;有一定的安全风险。 作为一…...

SQL注入 - CTF常见题型

文章目录 题型一 &#xff08; 字符型注入 &#xff09;题型二 &#xff08; 整数型注入 &#xff09;题型三 &#xff08; 信息收集SQL注入&#xff09;题型四 &#xff08; 万能密码登录 &#xff09;题型五 &#xff08; 搜索型注入文件读写 &#xff09;题型六 &#xff08…...

Keil 中设置 STM32 Flash 和 RAM 地址详解

文章目录 Keil 中设置 STM32 Flash 和 RAM 地址详解一、Flash 和 RAM 配置界面(Target 选项卡)1. IROM1(用于配置 Flash)2. IRAM1(用于配置 RAM)二、链接器设置界面(Linker 选项卡)1. 勾选“Use Memory Layout from Target Dialog”2. 查看链接器参数(如果没有勾选上面…...

学习STC51单片机31(芯片为STC89C52RCRC)OLED显示屏1

每日一言 生活的美好&#xff0c;总是藏在那些你咬牙坚持的日子里。 硬件&#xff1a;OLED 以后要用到OLED的时候找到这个文件 OLED的设备地址 SSD1306"SSD" 是品牌缩写&#xff0c;"1306" 是产品编号。 驱动 OLED 屏幕的 IIC 总线数据传输格式 示意图 …...

Linux云原生安全:零信任架构与机密计算

Linux云原生安全&#xff1a;零信任架构与机密计算 构建坚不可摧的云原生防御体系 引言&#xff1a;云原生安全的范式革命 随着云原生技术的普及&#xff0c;安全边界正在从传统的网络边界向工作负载内部转移。Gartner预测&#xff0c;到2025年&#xff0c;零信任架构将成为超…...

【JavaWeb】Docker项目部署

引言 之前学习了Linux操作系统的常见命令&#xff0c;在Linux上安装软件&#xff0c;以及如何在Linux上部署一个单体项目&#xff0c;大多数同学都会有相同的感受&#xff0c;那就是麻烦。 核心体现在三点&#xff1a; 命令太多了&#xff0c;记不住 软件安装包名字复杂&…...

【分享】推荐一些办公小工具

1、PDF 在线转换 https://smallpdf.com/cn/pdf-tools 推荐理由&#xff1a;大部分的转换软件需要收费&#xff0c;要么功能不齐全&#xff0c;而开会员又用不了几次浪费钱&#xff0c;借用别人的又不安全。 这个网站它不需要登录或下载安装。而且提供的免费功能就能满足日常…...

基于Springboot+Vue的办公管理系统

角色&#xff1a; 管理员、员工 技术&#xff1a; 后端: SpringBoot, Vue2, MySQL, Mybatis-Plus 前端: Vue2, Element-UI, Axios, Echarts, Vue-Router 核心功能&#xff1a; 该办公管理系统是一个综合性的企业内部管理平台&#xff0c;旨在提升企业运营效率和员工管理水…...

C++ 设计模式 《小明的奶茶加料风波》

&#x1f468;‍&#x1f393; 模式名称&#xff1a;装饰器模式&#xff08;Decorator Pattern&#xff09; &#x1f466; 小明最近上线了校园奶茶配送功能&#xff0c;业务火爆&#xff0c;大家都在加料&#xff1a; 有的同学要加波霸 &#x1f7e4;&#xff0c;有的要加椰果…...

LCTF液晶可调谐滤波器在多光谱相机捕捉无人机目标检测中的作用

中达瑞和自2005年成立以来&#xff0c;一直在光谱成像领域深度钻研和发展&#xff0c;始终致力于研发高性能、高可靠性的光谱成像相机&#xff0c;为科研院校提供更优的产品和服务。在《低空背景下无人机目标的光谱特征研究及目标检测应用》这篇论文中提到中达瑞和 LCTF 作为多…...

拟合问题处理

在机器学习中&#xff0c;核心任务通常围绕模型训练和性能提升展开&#xff0c;但你提到的 “优化训练数据解决过拟合” 和 “提升泛化性能解决欠拟合” 需要结合更准确的概念进行梳理。以下是对机器学习核心任务的系统复习和修正&#xff1a; 一、机器学习的核心任务框架 机…...

ubuntu中安装conda的后遗症

缘由: 在编译rk3588的sdk时&#xff0c;遇到编译buildroot失败&#xff0c;提示如下&#xff1a; 提示缺失expect&#xff0c;但是实测相关工具是在的&#xff0c;如下显示&#xff1a; 然后查找借助各个ai工具&#xff0c;重新安装相关的工具&#xff0c;依然无解。 解决&am…...