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

在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>

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

不一样的网络协议-------KCP协议

1、kcp 的协议特点 1.1、RTO 不翻倍 RTO(Retransmission TimeOut)&#xff0c;重传超时时间。tcp x 2&#xff0c;kcp x 1.5&#xff0c;提高传输速度 1.2、选择重传 TCP丢包时会全部重传从该包开始以后的数据&#xff0c;而KCP选择性重传&#xff0c;只重传真正丢失的数据包…

同为科技(TOWE)自动断电倒计时定时桌面PDU插排

在每个家庭中&#xff0c;插排插座都是必不可少的电源设备。随着各种电器的普及应用和生活节奏的加快&#xff0c;人们对插排也有着多样化的需求&#xff0c;比如在插排中加入定时开关、自动断电、断电记忆、倒计时等等功能&#xff0c;让原本不支持智能家居的用电器秒变智能。…

DL4J无法下载MNIST数据集解决 Server returned HTTP response code: 403 for URL解决方法

报错情况 报错如下&#xff1a; 16:45:41.463 [main] INFO org.nd4j.nativeblas.Nd4jBlas - Number of threads used for OpenMP BLAS: 6 16:45:41.497 [main] INFO org.nd4j.linalg.api.ops.executioner.DefaultOpExecutioner - Backend used: [CPU]; OS: [Windows 10] 16:4…

Android 驱动学习调试

1 Android 驱动代码编译 参考https://www.sharetechnote.com/html/Linux_DeviceDriver_Programing.html#Device_Driver_HelloWorld编译ko文件调试驱动代码&#xff0c;将ko文件push到手机上验证 相关C文件testdriver.c #include <linux/init.h> #include <linux/mod…

云安全—kubelet攻击面

0x00 前言 虽然说总结的是kubelet的攻击面&#xff0c;但是在总结攻击面之前还是需要去了解kubelet的基本原理&#xff0c;虽然说我们浅尝即止&#xff0c;但是还是要以能给别人讲出来为基本原则。 其他文章: 云安全—K8s APi Server 6443 攻击面云安全—K8S API Server 未授…

08-Docker-网络管理

Docker 在网络管理这块提供了多种的网络选择方式&#xff0c;他们分别是桥接网络、主机网络、覆盖网络、MACLAN 网络、无桥接网络、自定义网络。 1-无桥接网络&#xff08;None Network&#xff09; 当使用无桥接网络时&#xff0c;容器不会分配 IP 地址&#xff0c;也不会连…

SpringCloud 微服务全栈体系(十)

第十章 RabbitMQ 一、初识 MQ 1. 同步和异步通讯 微服务间通讯有同步和异步两种方式&#xff1a; 同步通讯&#xff1a;就像打电话&#xff0c;需要实时响应。 异步通讯&#xff1a;就像发邮件&#xff0c;不需要马上回复。 两种方式各有优劣&#xff0c;打电话可以立即得…

Sci Immunol丨Tim-3 适配器蛋白 Bat3 是耐受性树突状细胞

今天和大家分享一篇发表于2022年3月的文章&#xff0c;题目为“Tim-3 adapter protein Bat3 acts as an endogenous regulator of tolerogenic dendritic cell function”&#xff0c;发表在《Sci Immunol》杂志上。文章主要研究了Tim-3和其适配蛋白Bat3在调节免疫应答中的作用…

以八数码问题为例实现A*算法的求解(未完结)

八数码&#xff1a; 在一个 33 的网格中&#xff0c;1∼8 这 8 个数字和一个 x 恰好不重不漏地分布在这 33 的网格中。 例如&#xff1a; 1 2 3 x 4 6 7 5 8在游戏过程中&#xff0c;可以把 x 与其上、下、左、右四个方向之一的数字交换&#xff08;如果存在&#xff09;。…

ssh远程登录服务

目录 1.1版本协商阶段 1.2密钥和算法协商阶段 1.3认证阶段(两种认证方法): 2.1.安装ssh 2.2.配置文件分析: 3.1配置ssh监听端口号 3.2拒绝以root身份登录服务器 3.3虚拟机之间实现免密登录 3.4xshell免密登录 SSH (Secure Shell Protocol,安全壳程序协议)由IETF的网络…

厦门万宾科技智能井盖监测仪器的作用如何?

越来越多的人们希望改善生活&#xff0c;走出农村走出大山&#xff0c;前往城市之中居住。由此城市的人口和车辆在不断增加&#xff0c;与之而来的是城市的交通压力越来越大&#xff0c;时常会出现道路安全隐患&#xff0c;这给城市未来发展和智慧城市建设都带来一定的难题&…

电脑开机显示器不亮?正确操作分享(4个方法)!

“我的电脑最近不知道为什么&#xff0c;每次开机后显示器都不亮&#xff0c;重启也没反应&#xff0c;有什么方法可以解决该问题吗&#xff1f;快帮帮我&#xff01;” 随着电脑在我们日常生活和工作中的广泛应用&#xff0c;出现问题时需要及时解决。在众多的电脑问题中&…

家政保洁团队服务预约小程序的效果如何

家政保洁可以是大公司也可以是小团队&#xff0c;但无论什么规格&#xff0c;其市场需求都是稳中有增&#xff0c;随着人们生活品质提升&#xff0c;其对居住环境/办公环境等都有一定要求&#xff0c;这意味着家政团队可以拓展同城乃至外地多个领域的生意。 但线下信息单一&am…

【Linux】第九站:make和makefile

文章目录 一、 Linux项目自动化构建工具make/Makefile1.make/makefile工作现象2.依赖关系与依赖方法3.如何清理4.为什么这里我们需要带上clean5.连续的make6.特殊符号 二、Linux下实现一个简单的进度条1.回车换行2.缓冲区3.倒计时的实现 一、 Linux项目自动化构建工具make/Make…

Mybatis—XML配置文件、动态SQL

学习完Mybatis的基本操作之后&#xff0c;继续学习Mybatis—XML配置文件、动态SQL。 目录 Mybatis的XML配置文件XML配置文件规范XML配置文件实现MybatisX的使用 Mybatis动态SQL动态SQL-if条件查询 \<if\>与\<where\>更新员工 \<set\>小结 动态SQL-\<forea…

RHCSA -- VMware虚拟机配置及破解密码

一、配置虚拟机 1、开启VMware&#xff08;自定义&#xff09; 2、设置虚拟机硬件兼容性&#xff08;默认&#xff09; 3、稍后安装虚拟机操作系统 4、选择为Linux的虚拟机 5、虚拟机机名 6、设置虚拟机处理器 7、设置虚拟机所连接的网络类型 8、选择磁盘类型 9、设置所选磁…

Linux--jdk,tomca,mysql安装、后端项目搭建

一、JDK和Tomcat的安装 1.JDK安装 直接上传到Linux服务器的&#xff0c;上传jdk、tomcat安装包 解压JDK安装包 //解压jdk tar -zxvf jdk-8u151-linux-x64.tar.gz 置环境变量(JAVA_HOME和PATH) vim /etc/profile 在文件末尾添加以下内容&#xff1a; //java environment expo…

【算法|滑动窗口No.4】leetcode 485.最大连续 1 的个数 487.最大连续 1 的个数 II 1004. 最大连续1的个数 III

个人主页&#xff1a;兜里有颗棉花糖 欢迎 点赞&#x1f44d; 收藏✨ 留言✉ 加关注&#x1f493;本文由 兜里有颗棉花糖 原创 收录于专栏【手撕算法系列专栏】【LeetCode】 &#x1f354;本专栏旨在提高自己算法能力的同时&#xff0c;记录一下自己的学习过程&#xff0c;希望…

APISpace 天气预报查询API接口案例代码

1.天气预报查询API产品介绍 APISpace 的 天气预报查询&#xff0c;支持全国以及全球多个城市的天气查询&#xff0c;包含国内3400个城市以及国际4万个城市的实况数据&#xff0c;同时也支持全球任意经纬度查询&#xff0c;接口会返回该经纬度最近的站点信息&#xff1b;更新频率…

英语语法,时态总结,16种时态

文章目录 前言总体说明过去时一般过去时过去进行时过去完成时过去完成进行时 现在时一般现在时现在进行时现在完成时现在完成进行时 将来时一般将来时将来进行时将来完成时将来完成进行时 过去将来时一般过去将来时过去将来进行时过去将来完成时过去将来完成进行时 前言 学了这…