Vue实现可拖拽边界布局

news2024/11/25 9:47:43

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 - 5
      this.rightWidth = window.innerWidth * 0.7 - 5
      this.topHeight = window.innerHeight * 0.5 - 5
      this.bottomHeight = window.innerHeight * 0.5 - 5
      console.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 - 5
    this.rightWidth = window.innerWidth * 0.8 - 5
    this.topHeight = window.innerHeight * 0.5 - 5
    this.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.coloradmin.cn/o/1265295.html

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

相关文章

C++单调向量(栈):好子数组的最大分数

作者推荐 利用广度优先或模拟解决米诺骨牌 题目 给你一个整数数组 nums &#xff08;下标从 0 开始&#xff09;和一个整数 k 。 一个子数组 (i, j) 的 分数 定义为 min(nums[i], nums[i1], …, nums[j]) * (j - i 1) 。一个 好 子数组的两个端点下标需要满足 i < k <…

Vue3+java开发组队功能

Vue3java开发系统组队功能 需求分析 创建用户可以创建一个队伍&#xff08;一个房间队长&#xff09;&#xff0c;设置队伍人数&#xff0c;队伍名称&#xff08;标题&#xff09;&#xff0c;描述&#xff0c;超时时间。搜索加入&#xff0c;用户可以加入未满的队伍&#xf…

分子骨架跃迁工具-DiffHopp 评测

一、文章背景介绍 DiffHopp模型发表在ICML 2023 Workshop on Computational Biology&#xff08;简称&#xff1a;2023 ICML-WCB&#xff09;上的文章。第一作者是剑桥计算机系的Jos Torge。 DiffHopp是一个专门针对骨架跃迁任务而训练的E3等变条件扩散模型。此外&#xff0c;…

三、详解桶排序以及排序内容大总结

详解桶排序以及排序内容大总结 文章目录 详解桶排序以及排序内容大总结堆堆的操作(大)heapinsert --- 调整成大根堆heapify --- 移除原根节点后&#xff0c;继续调整成大根堆堆中某个位置的数值发生改变 堆排序优化 堆练习比较器桶排序基数排序 堆 注&#xff1a;堆是一种特殊…

mongodb查询数据库集合的基础命令

基础命令 启动mongo服务 mongod -f /usr/local/mongodb/mongod.conf //注意配置文件路径停止mongo服务 关闭mongodb有三种方式&#xff1a; 一种是进入mongo后通过mongo的函数关闭&#xff1b; use admin db.shutdownServer()一种是通过mongod关闭&#xff1b; mongod --s…

算法分析-寻找假币题

一.题目需求 你手里有70枚重量相等的真金硬币&#xff0c;但你知道其中有一枚是假币&#xff0c;比其他金币轻。 你有一个平衡秤&#xff0c;你可以一次在两边放上任意数量的硬币&#xff0c;它会告诉你两边是否重量相同&#xff0c;或者如果不相同&#xff0c;哪边更轻。 问题…

C语言——输入两个正整数 m 和 n。求其最大公约数和最小公倍数。

#define _CRT_SECURE_NO_WARNINGS 1#include<stdio.h> int main() {int m, n;int i;int x 1;int y 0;printf("请输入两个正整数m和n&#xff1a;\n");scanf("%d,%d", &m, &n);for (i 1; i < m && i < n; i) {if (m % i 0 …

汉威科技全系列VOC气体检测产品,护航绿色低碳安全发展

可能很多人都不知道&#xff0c;危化品爆炸、城市光化学烟雾污染&#xff08;如英国伦敦烟雾事件&#xff09;、城市灰霾、温室效应、臭氧层空洞等问题背后的元凶都是VOC。VOC(Volatile Organic Compounds)即挥发性有机物&#xff0c;这类物质易挥发&#xff0c;且普遍具有毒性…

【PHP】MySQL简介与MySQLi函数(含PHP与MySQL交互)

文章目录 一、MySQL简介二、MySQLi函数1. 开启mysqli扩展&#xff1a;2. PHP MySQLi扩展的常用函数 三、PHP与MySQL交互0. 准备1. 创建连接&#xff08;mysqli_connect() &#xff09;连接mysql语法 2. 选择数据库&#xff08;mysqli_select_db()&#xff09;3. 在php中操作数据…

算法通关村-----数据流的中位数

数据流的中位数 问题描述 中位数是有序整数列表中的中间值。如果列表的大小是偶数&#xff0c;则没有中间值&#xff0c;中位数是两个中间值的平均值。 例如 arr [2,3,4] 的中位数是 3 。 例如 arr [2,3] 的中位数是 (2 3) / 2 2.5 。 实现 MedianFinder 类: MedianFin…

使用 DMA 在 FPGA 中的 HDL 和嵌入式 C 之间传输数据

使用 DMA 在 FPGA 中的 HDL 和嵌入式 C 之间传输数据 该项目介绍了如何在 PL 中的 HDL 与 FPGA 中的处理器上运行的嵌入式 C 之间传输数据的基本结构。 介绍 鉴于机器学习和人工智能等应用的 FPGA 设计中硬件加速的兴起&#xff0c;现在是剥开几层“云雾”并讨论 HDL 之间来回传…

高校学生宿舍公寓报修维修生活管理系统 微信小程序b2529

本课题要求实现一套基于微信小程序宿舍生活管理系统&#xff0c;系统主要包括&#xff08;管理员&#xff0c;学生、维修员和卫检员&#xff09;四个模块等功能。 使用基于微信小程序宿舍生活管理系统相对传统宿舍生活管理系统信息管理方式具备很多优点&#xff1a;首先可以大幅…

ESP32-Web-Server 实战编程-通过网页控制设备多个 GPIO

ESP32-Web-Server 实战编程-通过网页控制设备多个 GPIO 概述 上节 ESP32-Web-Server 实战编程-通过网页控制设备的 GPIO 讲述了如何通过网页控制一个 GPIO。本节实现在网页上控制多个 GPIO。 示例解析 前端设计 前端代码建立了四个 GPIO&#xff0c;如下死 GPIO 2 在前端的…

【Java Spring】Spring MVC基础

文章目录 1、Spring MVC 简介2、Spring MVC 功能1.1 Spring MVC 连接功能2.2 Spring MVC 获取参数2.2.1 获取变量2.2.2 获取对象2.2.3 RequestParam重命名后端参数2.2.4 RequestBody 接收Json对象2.2.5 PathVariable从URL中获取参数 1、Spring MVC 简介 Spring Web MVC是构建于…

【计算机网络笔记】以太网

系列文章目录 什么是计算机网络&#xff1f; 什么是网络协议&#xff1f; 计算机网络的结构 数据交换之电路交换 数据交换之报文交换和分组交换 分组交换 vs 电路交换 计算机网络性能&#xff08;1&#xff09;——速率、带宽、延迟 计算机网络性能&#xff08;2&#xff09;…

鸿蒙开发学习——应用程序框架

文章目录 UIAbility的生命周期Create状态WindowStageCreateForeground和Background前后台展示控制onWindowStageDestroyDestory 总结 UIAbility的生命周期 感觉这里他讲的不清晰&#xff0c;UIAbility的4个声明周期是Create、Foreground&#xff08;桌面展示&#xff09;、Back…

C#-认识串口通信并使用串口助手

串口通讯(Serial Communication)&#xff0c;是指外设和计算机间&#xff0c;通过数据信号线、地线等&#xff0c;按位进行传输数据的一种双向通讯方式。 串口是一种接口标准&#xff0c;它规定了接口的电气标准&#xff0c;没有规定接口插件电缆以及使用的通信协议&#xff0…

ModbusRTU\TCP消息帧解析(C#实现报文发送与解析)

目录 知识点常用链接一、Modbus1.ModbusRTU消息帧解析2.主站poll、从站slave通讯仿真-modbusRTU1.功能码01读线圈状态2.功能码03读保持寄存器报文解析&#xff08;寄存器存整型&#xff09;报文解析&#xff08;寄存器存float&#xff09; 3.C#模拟主站Poll&#xff08;ModbusR…

P24 C++ 字符串常量

前言 本期我们讨论字符串字面量。 这是一种基于字符串的东西&#xff0c;上一章我们讲过字符串&#xff0c;你一定要去先去看看那一期的内容。 P23 C字符串-CSDN博客 01 什么是字符串字常量呢&#xff1f; 字符串字面量就是在双引号之间的一串字符 在上面的代码中&#xf…

Linux CentOS_7解决无法上网的问题

参考视频&#xff1a;保姆式教学虚拟机联网liunx(centos)_哔哩哔哩_bilibili 配置网络&#xff1a;解决上网问题 第一步&#xff1a;选择网络模式 第二步&#xff1a;配置网卡命令&#xff1a;打开终端执行命令&#xff1a; 1、先切换到根目录下&#xff0c;防止在第执行cd …