vue自定义组件:中线分割拖动盘

news2024/11/25 13:21:18

在GitHub上可以找到类似的组件,比如4年前发布的vue2版本的 Vue Split Pane,
但是我还是自己写了一个类似的:

组件效果:

在这里插入图片描述

特点:

  1. 不是照抄别人的。
  2. 同时支持vue2、vue3(组件内部使用选项式API风格
  3. 注释全,易扩展。

源码:
新建一个名为MiddleMoveLine.vue的文件,在使用处引入声明即可


<template>
  <!--  重新优化了整体实现逻辑...一个盘 分成A/B 两份---
  需要先了解定位布局的[子绝父相]---子绝对定位/父相对定位(最常用的搭配),才能理解本组件中的布局
  另外,只有定位元素才能设置z-index---不设置就是类似入栈行为。先写的被压在下面。

  usage:嵌套即可

  <MiddleMoveLine :safe-distance="[50,50]" :percent-a="30">
    <template v-slot:paneA>
      <div style="background-color: #5ee012;width: 100%;height: 100%;font-size: 50px">我是第一个panA</div>
    </template>

    <template v-slot:paneB>

      <MiddleMoveLine direction="y" :safe-distance="[50,50]" :percent-a="60">
        <template v-slot:paneA>
          <div style="background-color: #c512e0;width: 100%;height: 100%;font-size: 50px">我是第二个panA</div>
        </template>

        <template v-slot:paneB>
          <div style="background-color: #c70b2c;width: 100%;height: 100%;font-size: 50px">我是第二个panB</div>

        </template>
      </MiddleMoveLine>
    </template>
  </MiddleMoveLine>
  -->
  <div class="line-pane-view" :class="{cursor}" ref="line-pane" @mouseup="mouseUp" @mousemove="mouseMove">
    <div :class="[direction==='x'?'pane-ax':'pane-ay']" :style="paneABstyle[0]">
      <slot name="paneA"></slot>
    </div>
    <div class="middle-line active" ref="mouse-ctrl" :style="middleLineStyle"
         :class="[direction==='x'?'middle-line-x':'middle-line-y']"
         @mousedown="mouseD">
    </div>
    <div :class="[direction==='x'?'pane-bx':'pane-by']" :style="paneABstyle[1]">
      <slot :style="{'z-index':zIndex}" name="paneB"></slot>
    </div>
  </div>
</template>

<script>

export default {
  name: "MiddleMoveLine",
  props: {//父组件传递过来的数据
    direction: {// 描述分割中线的方向、样子
      default: 'x' // x--中线在x轴即左右方向分割成 x 方向上的 A、B两份
    },
    safeDistance: {
      default: function () {
        return [30, 40]// A的安全距离30px,B的安全距离40px
      }
    },
    zIndex: {// 第二个元素的层级,因为往往第二个压住第一个,所以可能需要加这个改变层级
      default: 1
    },
    percentA: {
      default: 55// A占比55%,B占比45% = 1 - 50%
    },
  },
  data() {
    return {
      active: false,

      percentX: 20,// 20% 中线 x
      percentY: 10,// 中线为 y 时使用

      mouseX: 0,//鼠标在 line-pane-view 坐标中的位置
      mouseY: 0,
      parentWidth: 0,//父元素line-pane-view 的宽高
      parentHeight: 0
    };
  },
  mounted() {
    this.percentX = this.percentA
    this.percentY = 100 - this.percentA
  },
  computed: {
    paneABstyle() {// 通过样式改变A、B的长宽度
      // x--中线左右方向分割, 此时A、B 左右 关系,移动中线的时候应该改变A、B的宽度
      const x = this.percentX;
      const y = this.percentY;
      if (this.direction === 'x') {// 同时返回 A、B 的样式
        return [{['width']: x + '%'}, {['width']: 100 - x + '%'}]
      } else {
        return [{['height']: y + '%'}, {['height']: 100 - y + '%'},]
      }
    },
    middleLineStyle() {//中线也需要同步更改
      const left = this.percentX
      const top = this.percentY
      if (this.direction === 'x') {
        return {['left']: left + "%"}
      } else {
        return {['top']: top + "%"}
      }
    },
    cursor() {// 根据是否激活来设置鼠标的拖动样式
      return this.active ? (this.direction === 'x' ? 'row-resize' : 'col-resize') : ''
    },
  },
  methods: {
    mouseMove(e) {
      // 鼠标没有被按下 或者 没有被触发
      if (e.buttons === 0 || e.which === 0) {
        this.active = false
      }
      if (!this.active) return;
      // console.log("鼠标移动:", e,"e.currentTarget:",e.currentTarget);
      const parentRect = e.currentTarget.getBoundingClientRect();
      let x = e.clientX - parentRect.left;

      let y = e.clientY - parentRect.top;
      const w = parentRect.width;
      const h = parentRect.height;
      const safeDistance = this.safeDistance

      // 判断bu在安全距离内,例外处理:
      x = x < safeDistance[0] ? safeDistance[0] : x
      x = x > (w - safeDistance[1]) ? (w - safeDistance[1]) : x
      y = y < safeDistance[0] ? safeDistance[0] : y
      y = y > (h - safeDistance[1]) ? (h - safeDistance[1]) : y

      this.mouseX = x
      this.mouseY = y
      this.parentWidth = w;
      this.parentHeight = h;
      console.log("鼠标移动:this.mouseX,Y", this.mouseX, this.mouseY, "this.parentWidth,H:", this.parentWidth, this.parentHeight);
      console.log("鼠标移动:left,top:", (this.mouseX / this.parentWidth) * 100 + "%", (this.mouseY / this.parentHeight) * 100 + "%");

      this.percentX = (x / w) * 100;
      this.percentY = (y / h) * 100;
      //   可以向父组件发送事件,让父组件可以处理其他组件比例来适应
      this.$emit('change-view-size', {x, y, w, h})

    },
    mouseUp() {
      console.log("鼠标松开---整个面板的事件");
      if (this.active)
        this.active = false
    },
    // 中线的鼠标事件
    mouseD() {
      console.log("鼠标按下");
      if (!this.active)
        this.active = true
    },
  },
  watch: {
    // 暂时没有这个动态更改初始比例的需求...
    // percentA(newVal, oldVal) {// vue2中初次加载不会运行(不认为数据发生变化),运行中改变数据才能运行// vue3中可以在初始化的时候运行(具体配置看vue3官网)
    //   // console.log("percentA 发生变化了...", newVal, oldVal)
    //   // this.percentX = newVal
    //   // this.percentY = 100 - newVal
    // },
  }
}
;
</script>

<style scoped lang="scss">
.line-pane-view {
  width: 100%;
  height: 100%;
  position: relative; /*子绝父相---由于孩子都是绝对的,父亲必须有定位才能困住*/
  user-select: none;
}

.middle-line {
  /* 设置元素的定位方式为绝对定位,相对于最近的定位上下文进行定位 */
  position: absolute;
  /* 设置盒模型为 border-box,元素的宽度和高度包括内容、内边距和边框 */
  //-moz-box-sizing: border-box; /* 适用于 Firefox 浏览器 */
  //-webkit-box-sizing: border-box; /* 适用于 WebKit 内核浏览器(如 Chrome、Safari) */
  box-sizing: border-box; /* 标准写法,适用于大多数现代浏览器 */
  background-color: #0D0D0D;
  /* 设置元素的不透明度为 0.1,取值范围为 0(完全透明)到 1(完全不透明)之间 */
  opacity: .1;
  /* 设置元素的层叠顺序,用于控制元素在堆叠顺序中的显示优先级 */
  z-index: 2;
  /* 设置背景剪切方式为 padding-box,背景仅绘制在内边距区域 */
  -moz-background-clip: padding; /* 适用于 Firefox 浏览器 */
  -webkit-background-clip: padding; /* 适用于 WebKit 内核浏览器(如 Chrome、Safari) */
  background-clip: padding-box; /* 标准写法,适用于大多数现代浏览器 */
}


/*x--中线左右方向分割 */
.middle-line-x {
  width: 11px;
  height: 100%;
  margin-left: -5px;
  border-left: 5px solid rgba(255, 255, 255, 0);
  border-right: 5px solid rgba(255, 255, 255, 0);
  cursor: col-resize;
}

.middle-line-y {
  height: 11px;
  margin: -5px 0;
  border-top: 5px solid rgba(255, 255, 255, 0);
  border-bottom: 5px solid rgba(255, 255, 255, 0);
  cursor: row-resize;
  width: 100%;
}

// A 代表上、左  | B 代表下、右
.pane-ay {
  position: absolute;
  top: 0; /*上 --- 中线为 y 时*/
  width: 100%;
}

.pane-by {
  position: absolute;
  bottom: 0; /*下 --- 中线为 y 时*/
  width: 100%;
  padding-top: 3px;
}

.pane-ax {
  position: absolute;
  left: 0; /*左 --- 中线为 x 时*/
  height: 100%;
  padding-right: 3px;
}

.pane-bx {
  position: absolute;
  right: 0; /*右 --- 中线为 x 时*/
  height: 100%;
  padding-left: 3px;
}

.active:hover {
  background-color: mediumvioletred;

}

.middle-move-line.active {
  background-color: #575555;
}
</style>

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

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

相关文章

【JavaScript保姆级教程】switch分支与while循环

文章目录 前言一、Switch分支1.1 switch基本结构1.2 break语句1.3 default标签1.4 下面是几个Switch分支的示例代码&#xff1a;示例1: 根据星期数输出对应的中文星期名称示例2: 根据用户输入的颜色选择执行不同的操作 二、While循环&#xff1a;2.1 while循环基本格式2.2 cont…

学习性能测试线路图

性能测试学习线路图(建议) 1、概览 纵向划分3颗子树:vugen,controller,monitor。优先学习vugen脚本开发以及调试。 横向划分为2层&#xff1a;基础知识以及高级应用。 2、基础知识 2.1、Loadrunner工具使用 2.1.1、建议学习路径 Vugen开发脚本&#xff08;函数使用&#x…

2.2 - 网络协议 - IP协议,IP地址划分,报文格式,数据分片,抓包实战

「作者主页」&#xff1a;士别三日wyx 「作者简介」&#xff1a;CSDN top100、阿里云博客专家、华为云享专家、网络安全领域优质创作者 「推荐专栏」&#xff1a;对网络安全感兴趣的小伙伴可以关注专栏《网络安全入门到精通》 IP协议 1、IP地址划分2、IP协议报文格式3、IP协议数…

Git(七).git 文件夹瘦身,GitLab 永久删除文件

目录 一、问题背景二、问题复现2.1 新建项目2.2 上传大文件2.3 上传结果 三、解决方案3.1 GitLab备份与还原1&#xff09;备份2&#xff09;还原 3.2 删除方式一&#xff1a;git filter-repo 命令【推荐】1&#xff09;安装2&#xff09;删除本地仓库文件3&#xff09;重新关联…

图的广度优先遍历的单源路径、无权图的最短路径问题、BFS性质附Java代码

目录 使用BFS求解单源路径问题 BFS重要性质 无权图的最短路径问题 使用BFS求解单源路径问题 import java.util.ArrayList; import java.util.Collections; import java.util.LinkedList; import java.util.Queue;public class SingleSourcePath {private Graph G;private i…

Greenplum管理和监控工具-gpcc-web介绍

Greenplum管理和监控工具-gpcc-web介绍 1. gpcc-web简介 ​ gpcc&#xff08;Greenplum Command Center&#xff09;的Web用户界面是一个强大的工具&#xff0c;它可以帮助用户管理Greenplum数据库集群&#xff0c;提高效率&#xff0c;优化性能&#xff0c;并确保数据的安全…

安防监控项目---Cortex-A9和zigbee传感器数据上传至网页

文章目录 前言一、A9平台数据的采集与上传二、共享内存上传数据到CGI三、设备代码总结 前言 书接上期&#xff0c;我们大概来梳理一下&#xff0c;已经完成的需求有哪些了&#xff0c;从html下发指令控制Cortex-A9平台硬件&#xff0c;其中主要实现的有控制LED,蜂鸣器&#xf…

AI大模型时代网络安全攻防对抗升级,瑞数信息变革“下一代应用与数据安全”

AI与大模型技术加速普及&#xff0c;安全领域也在以创新视角聚焦下一代应用安全WAAP变革&#xff0c;拓展新一代数据安全领域。近日瑞数信息重磅发布了瑞数全新API扫描器、API安全审计、数据安全检测与应急响应系统及分布式数据库备份系统四大新品。此次发布在延续瑞数信息Bot自…

【中国知名企业高管团队】系列55:奇瑞汽车

昨天华研荟介绍了吉利集团的现状&#xff0c;创始人李书福先生的故事&#xff0c;以及吉利集团的现任高管团队。我们了解到现在的吉利集团品牌多元化&#xff0c;吉利汽车只是其中的一个品牌和产业集团&#xff0c;整个吉利集团有十余个汽车品牌&#xff0c;还有多个网约车品牌…

力扣第416题 *** 分割等和子集 c++ 新题 动态规划 中的 01背包问题

题目 416. 分割等和子集 中等 相关标签 数组 动态规划 给你一个 只包含正整数 的 非空 数组 nums 。请你判断是否可以将这个数组分割成两个子集&#xff0c;使得两个子集的元素和相等。 示例 1&#xff1a; 输入&#xff1a;nums [1,5,11,5] 输出&#xff1a;true 解释…

qt 系列(二)---qt designer通过设置控件样式表进行背景颜色设置

1. 前言 一般Layouts不可以进行改变样式表&#xff0c;当我们想修改背景样式表&#xff0c;同时又不改变其他控件的颜色时&#xff0c;可以选择List View 控件改变背景颜色。 2. 设置背景 &#xff08;1&#xff09;配置 .qrc 文件 新建mypicture.qrc文件&#xff0c;记事本打…

【刷题宝典NO.0】

目录 素数的判定 打印素数 打印水仙花数 百钱买百坤 输出闰年 逆序打印一个整数的每一位 输出乘法口诀表 数字9出现的次数 二进制1的个数 输出一个整数的偶数位和奇数位的二进制序列 求两个整数的最大公约数 求两个整数的最小公倍数 小乐乐与欧几里得 小…

C#,数值计算——积分方程与逆理论Quad_matrix的计算方法与源程序

1 文本格式 using System; namespace Legalsoft.Truffer { public class Quad_matrix : UniVarRealMultiValueFun { private int n { get; set; } private double x { get; set; } public Quad_matrix(double[,] a) { this.n a…

适用于 Linux 的 WPF:Avalonia

许多年前&#xff0c;在 WPF 成为“Windows Presentation Foundation”并将 XAML 作为 .NET、Windows 等的 UI 标记语言引入之前&#xff0c;有一个代号为“Avalon”的项目。Avalon 是 WPF 的代号。XAML 现在无处不在&#xff0c;XAML 标准是一个词汇规范。 Avalonia 是一个开…

精品Python空巢老人志愿服务平台慈善捐赠活动报名

《[含文档PPT源码等]精品基于Python的空巢老人志愿服务平台》该项目含有源码、文档、PPT、配套开发软件、软件安装教程、项目发布教程、包运行成功&#xff01; 软件开发环境及开发工具&#xff1a; 开发语言&#xff1a;python 使用框架&#xff1a;Django 前端技术&#…

EasyExcel复杂表头数据导入

目录 表头示例导入代码数据导出 表头示例 导入代码 Overridepublic void importExcel(InputStream inputStream) {ItemExcelListener itemExcelListener new ItemExcelListener();EasyExcel.read(inputStream, ImportItem.class, itemExcelListener).headRowNumber(2).sheet()…

【蓝桥每日一题]-二分类型(保姆级教程 篇2) #砍树 #木材加工

今天讲二分的例题&#xff0c;一道是“砍树”&#xff0c;一道是“木材加工” 目录 题目&#xff1a;砍树 思路1&#xff1a; 思路2&#xff1a; 题目&#xff1a;木材加工 思路&#xff1a; 题目&#xff1a;砍树 思路1&#xff1a; 二分查找&#xff1a;对高度进行二分 二…

关注云栖大会的感受:从工业大脑到全面AI时代的进化

前言 自2009年的地方网站峰会到如今的云栖大会&#xff0c;这个盛大的科技盛事已经走过了一个多十年的漫长历程。这个会议见证了中国科技行业的崛起&#xff0c;也记录了技术的不断演化。而对我来说&#xff0c;首次接触云栖大会是在2020年&#xff0c;当年大会迁移到线上&…

从零开发基于ASM字节码的Java代码混淆插件XHood

项目背景 因在公司负责基础框架的开发设计&#xff0c;所以针对框架源代码的保护工作比较重视&#xff0c;之前也加入了一系列保护措施 例如自定义classloader加密保护&#xff0c;授权license保护等&#xff0c;但都是防君子不防小人&#xff0c;安全等级还比较低 经过调研…

【密评】商用密码应用安全性评估从业人员考核题库(十七)

商用密码应用安全性评估从业人员考核题库&#xff08;十七&#xff09; 国密局给的参考题库5000道只是基础题&#xff0c;后续更新完5000还会继续更其他高质量题库&#xff0c;持续学习&#xff0c;共同进步。 4001 多项选择题 网络和通信安全层面的通信主体一般包括哪些&…