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>

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

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

相关文章

ANN人工神经网络:从基础认知到现实理解

什么是神经网络&#xff1f; 神经网络的再认知 前面我们了解过&#xff0c;人工神经网络&#xff08;Artificial Neural Network&#xff0c;ANN&#xff09;是人类为了模仿人大脑的神经网络结构创建出来的一种计算机系统结构。但如果仔细深入到神经网络当中&#xff0c;会慢…

排序算法基本原理及实现1

&#x1f4d1;打牌 &#xff1a; da pai ge的个人主页 &#x1f324;️个人专栏 &#xff1a; da pai ge的博客专栏 ☁️宝剑锋从磨砺出&#xff0c;梅花香自苦寒来 &#x1f4d1;插入排序 &#x1f4…

JAVAEE---多线程线程安全

根本原因&#xff1a;随机调度&#xff0c;抢占式执行 多个线程同时修改同一个变量 修改操作不是原子的 内存可见性 指令重排序 上面这段代码可以正常打印出hello&#xff0c;按照我们前面所学&#xff0c;第一次加锁之后&#xff0c;第二次加锁应该有所冲突啊。这里是因为…

c++ day2

自己封装一个矩形类(Rect)&#xff0c;拥有私有属性:宽度(width)、高度(height)&#xff0c; 定义公有成员函数: 初始化函数:void init(int w, int h) 更改宽度的函数:set_w(int w) 更改高度的函数:set_h(int h) 输出该矩形的周长和面积函数:void show() #ifndef RECT_H …

量子力学应用:探索科技前沿的奇幻之旅

量子力学应用:探索科技前沿的奇幻之旅 引言 量子力学,这门探讨微观世界规律的学科,自其诞生以来就充满了神秘与奇幻。随着科学技术的不断进步,量子力学已经从纯理论研究走向了实际应用领域,为我们打开了一个全新的科技世界。在本文中,我们将深入探讨量子力学的应用方面,…

JS 绘制半径不一致的环形图进度条

HTML部分: <canvas id"mycanvas" width"100" height"100"></canvas>JS部分&#xff1a; const option {element: "mycanvas", // 元素count: 26, // 高亮数据totalCount: 129, // 总数据progressColor: #3266FB, // 进…

一文详解Python中常用数据类型

文章目录 Python 中常用的数据类型包括&#xff1a;Python 中布尔类型(bool)Python 中的数字类型概述Pyhon中的字符串概述Python 中的List概述Python 中的元组类型(tuple)Python中的字典&#xff08;Dictionary&#xff09;Python中的集合&#xff08;Set&#xff09;Python中的…

你了解vue的diff算法吗?

​&#x1f308;个人主页&#xff1a;前端青山 &#x1f525;系列专栏&#xff1a;Vue篇 &#x1f516;人终将被年少不可得之物困其一生 依旧青山,本期给大家带来vue篇专栏内容:vue中的diff算法 目录 一、是什么 二、比较方式 三、原理分析 小结 一、是什么 diff 算法是一…

【蓝桥杯选拔赛真题69】Scratch洗牌发牌 少儿编程scratch图形化编程 蓝桥杯创意编程选拔赛真题解析

目录 scratch洗牌发牌 一、题目要求 编程实现 二、案例分析 1、角色分析

(C++)字符串相乘

个人主页&#xff1a;Lei宝啊 愿所有美好如期而遇 题目链接如下&#xff1a; 力扣&#xff08;LeetCode&#xff09;官网 - 全球极客挚爱的技术成长平台备战技术面试&#xff1f;力扣提供海量技术面试资源&#xff0c;帮助你高效提升编程技能&#xff0c;轻松拿下世界 IT 名…

【c语言:常用字符串函数与内存函数的使用与实现】

文章目录 1. strlen函数1.1使用1.2模拟实现 2.strcmp函数2.1使用2.2模拟实现 3.strncmp函数3.1使用3.2模拟实现 4.strcpy函数4.1 使用4.2模拟实现 5.strcncpy5.1使用5.2模拟实现 6.strcat函数6.1使用6.2模拟实现 7.strncat函数7.1使用7.2模拟实现 8.strstr函数8.1使用8.2模拟实…

1 NLP分类之:FastText

0 数据 https://download.csdn.net/download/qq_28611929/88580520?spm1001.2014.3001.5503 数据集合&#xff1a;0 NLP: 数据获取与EDA-CSDN博客 词嵌入向量文件&#xff1a; embedding_SougouNews.npz 词典文件&#xff1a;vocab.pkl 1 模型 基于fastText做词向量嵌入…

vue3+vite+ts项目打包时出错

项目中引入了element-plus国家化的配置&#xff0c;然后进行项目打包&#xff0c;报下面的错误 解决方法&#xff1a; 在main.ts中添加 // ts-ignore

【存储】blotdb的原理及实现(2)

【存储】etcd的存储是如何实现的(3)-blotdb 在etcd系列中&#xff0c;我们对作为etcd底层kv存储的boltdb进行了比较全面的介绍。但是还有两个点没有涉及。 第一点是boltdb如何和磁盘文件交互。 持久化存储和我们一般业务应用程序的最大区别就是其强依赖磁盘文件。一方面文件数…

Linux系统之一次性计划任务at命令的基本使用

Linux系统之一次性计划任务at命令的基本使用 一、at命令介绍二、at命令的使用帮助2.1 at命令的help帮助信息2.2 at命令的语法解释 三、at命令的日常使用3.1 立即执行一次性任务3.2 指定时间执行一次性任务3.3 查询计划任务3.4 其他指定时间用法3.5 删除已经设置的计划任务3.6 显…

深度学习毕设项目 基于生成对抗网络的照片上色动态算法设计与实现 - 深度学习 opencv python

文章目录 1 前言1 课题背景2 GAN(生成对抗网络)2.1 简介2.2 基本原理 3 DeOldify 框架4 First Order Motion Model 1 前言 &#x1f525; 这两年开始毕业设计和毕业答辩的要求和难度不断提升&#xff0c;传统的毕设题目缺少创新和亮点&#xff0c;往往达不到毕业答辩的要求&am…

echarts案例网站

一、ppchart 网站&#xff1a;https://ppchart.com/#/ 二、echarts官网示例 网站&#xff1a;https://echarts.apache.org/examples/zh/index.html

1992-2021年区县经过矫正的夜间灯光数据(GNLD、VIIRS)

1992-2021年区县经过矫正的夜间灯光数据&#xff08;GNLD、VIIRS&#xff09; 1、时间&#xff1a;1992-2021年3月&#xff0c;其中1992-2013年为年度数据&#xff0c;2013-2021年3月为月度数据 2、来源&#xff1a;DMSP、VIIRS 3、范围&#xff1a;区县数据 4、指标解释&a…

NeurIPS 2023|AI Agents先行者CAMEL:第一个基于大模型的多智能体框架

AI Agents是当下大模型领域备受关注的话题&#xff0c;用户可以引入多个扮演不同角色的LLM Agents参与到实际的任务中&#xff0c;Agents之间会进行竞争和协作等多种形式的动态交互&#xff0c;进而产生惊人的群体智能效果。本文介绍了来自KAUST研究团队的大模型心智交互CAMEL框…

双指针算法(题目与答案讲解)

文章目录 题目移动零复写零两数之和N数之和(>2个数) 答案讲解移动零复写零两数之和N数之和 题目 力扣 移动零 1、移动零:题目链接 复写零 2、复写零:题目链接 两数之和 3、两数之和题目链接 N数之和(>2个数) 4、N数之和(三个数、四个数) 三个数:题目链接 四个数题目链接…