突破编程_前端_JS编程实例(分割窗体组件)

news2025/1/11 7:10:10

1 开发目标

分隔窗体组件旨在提供灵活的窗体分隔功能,支持横向分割与纵向分隔两种类型,并具备拖拽调整窗体比例的功能,同时提供最小比例设置,以防止窗体被过度缩小:

在这里插入图片描述

2 详细需求

2.1 分隔窗体类型

(1)横向分割:

  • 用户可以在窗体顶部或底部添加一个横向分割条,将窗体分割成上下两部分。
  • 分割条的位置可以通过拖拽调整,以改变上下两部分窗体的高度比例。

(2)纵向分隔:

  • 用户可以在窗体左侧或右侧添加一个纵向分割条,将窗体分割成左右两部分。
  • 分割条的位置同样可以通过拖拽调整,以改变左右两部分窗体的宽度比例。

2.2 鼠标样式切换

(1)鼠标靠近状态:

  • 当鼠标指针移动到分割条附近的一定范围内时,分割条应自动变为拖拽样式。
  • 拖拽样式可以通过视觉上的变化来体现,例如改变分割条的颜色、形状或添加拖拽图标等。

(2)鼠标远离状态:

  • 当鼠标指针离开分割条附近的范围时,分割条应自动恢复到默认样式。
  • 默认样式应简洁明了,以便在不需要拖拽调整时保持窗体的整体美观性。

2.3 拖拽调整窗体比例

(1)拖拽过程:

  • 用户点击并拖动拖拽样式的分割条时,应能够实时改变窗体的比例。
  • 拖拽过程中,应提供平滑的过渡效果,确保窗体布局的调整连贯且自然。

(2)横向分割调整:

  • 在横向分割模式下,拖动分割条将改变上下两部分窗体的高度比例。
  • 用户可以通过向上或向下拖动分割条来调整上下窗体的相对大小。

(3)纵向分隔调整:

  • 在纵向分隔模式下,拖动分割条将改变左右两部分窗体的宽度比例。
  • 用户可以通过向左或向右拖动分割条来调整左右窗体的相对大小。

2.4 最小比例设置

(1)设置功能:

  • 组件应提供设置最小比例的功能,允许用户自定义窗体在分割调整时的最小比例限制。
  • 用户可以通过配置项或API接口来设置最小比例值。

(2)横向分割调整:

  • 当用户尝试通过拖拽将窗体调整到小于最小比例时,应阻止进一步的调整操作。
  • 此时,可以通过视觉反馈(如提示信息、分割条位置固定等)来告知用户已达到最小比例限制。

3 代码实现

首先创建一个 neat_spliterwidget.js 文件,该文件用于本组件的工具类、目录处理函数的代码构建。

(1)创建分隔窗体的基类:

首先,定义核心数据变量:

class NeatSpliterWidget {

    constructor(container,para) {

        this.container = container;
        this.para = para;

        this.wid1 = null;
        this.wid2 = null;

        this.spliterRatio = para.spliterRatio ?? 0.3;
        this.minSpace = 20;
        this.spliterSpace = 3;              // 切换鼠标样式的间距
        this.dragReadyFalg = false;
        this.dragActiveFlag = true;
        this.dragFalg = false;
        this.dragStart = 0;

        this.render();
    }

接下来,进行基础类型的渲染,包括创建子窗体:

	render() {
        this.container.style.display = 'flex';

        this.wid1Tmp = document.createElement('div');
        this.wid2Tmp = document.createElement('div');
        this.widSpliter = document.createElement('div');

        this.wid1 = document.createElement('div');
        this.wid1.style.width = '100%';
        this.wid1.style.height = '100%';
        this.wid1Tmp.appendChild(this.wid1);
        this.wid2 = document.createElement('div');
        this.wid2.style.width = '100%';
        this.wid2.style.height = '100%';
        this.wid2Tmp.appendChild(this.wid2);

    }

最后,定义鼠标事件,计算拖拽时的初始位置:

	initSpliterEvent() {
        let that = this;
        if (!that.dragActiveFlag) {
            return;
        }

        this.container.addEventListener("mousedown", function (event) {
            if (!that.dragActiveFlag) {
                return;
            }
            if('column' == that.container.style.flexDirection){
                that.dragStart = event.clientY - event.currentTarget.offsetTop;
            }else{
                that.dragStart = event.clientX - event.currentTarget.offsetLeft;
            }
            if (that.dragReadyFalg) {
                that.dragFalg = true;
                this.onselectstart = function () { return false; };
            } else {
                that.dragFalg = false;
                this.onselectstart = function () { return true; };
            }
        });


        this.container.addEventListener("mouseup", function (event) {
            if (!that.dragActiveFlag) {
                return;
            }
            if('column' == that.container.style.flexDirection){
                that.dragStart = event.clientY - event.currentTarget.offsetTop;
            }else{
                that.dragStart = event.clientX - event.currentTarget.offsetLeft;
            }
            that.dragFalg = false;
            this.onselectstart = function () { return true; };
        });

    }
}

(2)接下来,开始定义纵向分割窗体的组件(支持水平拖拽):

class NeaterHSpliterWidget extends NeatSpliterWidget {

    constructor(container,para) {
        super(container,para);
    }

    render() {
        super.render();

        this.wid1Tmp.style.width = (this.spliterRatio * 100).toString() + '%';
        this.wid1Tmp.style.height = '100%';
        this.wid2Tmp.style.width = '10px';
        this.wid2Tmp.style.height = '100%';
        this.wid2Tmp.style.flex = 1;
        this.widSpliter.style.height = '100%';
        this.widSpliter.style.width = '1px';
        this.widSpliter.style.borderLeft = '1px solid #CACDD1';
        this.container.appendChild(this.wid1Tmp);
        this.container.appendChild(this.widSpliter);
        this.container.appendChild(this.wid2Tmp);

        this.initSpliterEvent();
    }

上面代码定义了 NeaterHSpliterWidget 的渲染方式,主要是将子窗体以及分割条做水平布局,接下来是处理水平拖拽事件:

	initSpliterEvent() {
        super.initSpliterEvent();
        let that = this;
        if (!that.dragActiveFlag) {
            return;
        }
        this.container.addEventListener("mousemove", function (event) {
            let clientX = event.clientX - event.currentTarget.offsetLeft;
            if (that.dragFalg) {
                let dragOffset = clientX - that.dragStart;
                let spliterWidth1 = that.wid1Tmp.offsetWidth + dragOffset;
                if (spliterWidth1 < that.minSpace || (that.container.offsetWidth - spliterWidth1) < that.minSpace) {
                    return;
                }
                that.spliterRatio = spliterWidth1 / that.container.offsetWidth;
                that.wid1Tmp.style.width = spliterWidth1 + 'px';
                that.dragStart = clientX;
            } else {
                if (clientX > that.wid1Tmp.offsetWidth - that.spliterSpace && clientX < that.wid1Tmp.offsetWidth + that.spliterSpace + 1) {
                    that.container.style.cursor = "col-resize";
                    that.dragReadyFalg = true;
                } else {
                    that.container.style.cursor = "default";
                    that.dragReadyFalg = false;
                }
            }
        });

    }
}

上面代码的核心逻辑是计算更换鼠标样式的位置以及计算拖拽时分隔比例的变化。

(3)然后,定义横向分割窗体的组件(支持垂直拖拽):

class NeaterVSpliterWidget extends NeatSpliterWidget {

    constructor(container,para) {
        super(container,para);
    }

    render() {
        super.render();

        this.container.style.flexDirection = 'column';

        this.wid1Tmp.style.height = (this.spliterRatio * 100).toString() + '%';
        this.wid1Tmp.style.width = '100%';
        this.wid2Tmp.style.height = '10px';
        this.wid2Tmp.style.width = '100%';
        this.wid2Tmp.style.flex = 1;
        this.widSpliter.style.width = '100%';
        this.widSpliter.style.borderBottom = '1px solid #CACDD1';
        this.container.appendChild(this.wid1Tmp);
        this.container.appendChild(this.widSpliter);
        this.container.appendChild(this.wid2Tmp);

        this.initSpliterEvent();
    }

上面代码定义了 NeaterVSpliterWidget 的渲染方式,主要是将子窗体以及分割条做垂直布局,接下来是处理垂直拖拽事件:

	initSpliterEvent() {
        super.initSpliterEvent();
        let that = this;
        if (!that.dragActiveFlag) {
            return;
        }
        this.container.addEventListener("mousemove", function (event) {
            let clientY = event.clientY - event.currentTarget.offsetTop;
            if (that.dragFalg) {
                let dragOffset = clientY - that.dragStart;
                let spliterHeight1 = that.wid1Tmp.offsetHeight + dragOffset;
                if (spliterHeight1 < that.minSpace || (that.container.offsetHeight - spliterHeight1) < that.minSpace) {
                    return;
                }
                that.spliterRatio = spliterHeight1 / that.container.offsetHeight;
                that.wid1Tmp.style.height = spliterHeight1 + 'px';
                that.dragStart = clientY;
            } else {
                if (clientY > that.wid1Tmp.offsetHeight - that.spliterSpace && clientY < that.wid1Tmp.offsetHeight + that.spliterSpace + 1) {
                    that.container.style.cursor = "row-resize";
                    that.dragReadyFalg = true;
                } else {
                    that.container.style.cursor = "default";
                    that.dragReadyFalg = false;
                }
            }
        });

    }
}

上面代码的核心逻辑是计算更换鼠标样式的位置以及计算拖拽时分隔比例的变化。

至此,整个分割窗体组件构建结束。

(4)完成目录导航功能的组件的代码编写后,可以创建 neat_spliterwidget.html 文件,调用该组件:

<!DOCTYPE html>
<html>

<head>
    <meta charset="UTF-8" />
    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
    <meta http-equiv="X-UA-Compatible" content="ie=edge" />
    <title>spliter widget</title>
    <style>
        html {
            height: 100%;
        }

        body {
            margin: 0;
            height: 100%;
        }
    </style>
</head>

<body>
    <div id="divMain" style="height: 400px;width: 600px;margin: 20px;border: 1px solid #aaa;"></div>
</body>
<script src="./neat_spliterwidget.js"></script>
<script>
    let para = {
        spliterRatio:0.3,
    }
    let hSpliterWidget = new NeaterHSpliterWidget(document.getElementById('divMain'), para);

    para.spliterRatio = 0.7;
    let vSpliterWidget = new NeaterVSpliterWidget(hSpliterWidget.wid2, para);
    
</script>

</html>

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

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

相关文章

软件工程学习笔记14——案例解析篇

案例解析篇 一、大型开源项目对软件工程的应用1、开发迭代过程 二、大厂是怎样应用软件工程的1、软件项目开发团队组成&#xff08;1&#xff09;软件开发团队规模小&#xff08;2&#xff09;没有专职测试&#xff08;3&#xff09;DevOps 文化 2、开发工具的使用3、项目开发流…

结构体,联合体,枚举( 2 )

目录 2.联合体 2.1联合体类型的声明 2.2联合体的特点 2.3联合体的内存大小 3.枚举 3.1枚举类型的声明 3.2枚举类型的优点 3.3枚举类型的使用 2.联合体 联合体&#xff08;Union&#xff09;是另一种复合数据类型&#xff0c;它允许我们在同一内存位置存储不同的数据类型…

PyYAML,一个强大的 Python 库!

更多资料获取 &#x1f4da; 个人网站&#xff1a;ipengtao.com 大家好&#xff0c;今天为大家分享一个强大的 Python 库 - pyyaml。 Github地址&#xff1a;https://github.com/yaml/pyyaml/ 在处理配置文件、序列化数据等场景中&#xff0c;YAML&#xff08;YAML Ain’t Ma…

雷勒过滤与您相约2024第13届生物发酵展

参展企业介绍 青岛雷勒过滤科技有限公司是一家致力于不锈钢过滤网&#xff0c;涂料筛&#xff0c;滤袋等研发、生产和服务一体的综合型公司。雷勒专业生产各种高品质的不锈钢过滤元件&#xff0c;用于各种关键的过滤与分离环节。 雷勒拥有先进的生产设备&#xff0c;完善的产…

BGP-BGP选路、BGP4+

BGP-BGP选路&#xff0c;BGP4 支持IPv6的BGP&#xff0c;又称为MP-BGP&#xff0c;BGP4 BGP&#xff1a;外部网关协议、使用TCP作为其传输层协议、支持CIDR、增量更新、路径矢量路由协议、无环路、路由策略丰富、可防止路由震荡、易于扩展。 BGP工作原理——报文类型&#x…

信捷 XD/XL plc 单精度/双精度浮点数比较 ECMP,EDCMP

对于单精度浮点数&#xff0c;用ECMP指令。 对于双精度浮点数&#xff0c;用EDCMP指令 注意&#xff1a;EDCMP 指令中寄存器的首地址必须为偶数。

教育信创 | 云轴科技ZStack联合飞腾发布全场景教育信创白皮书

随着数字化时代的到来&#xff0c;教育行业正面临着前所未有的挑战与机遇。为了推动教育行业的数字化转型和信创人才培养&#xff0c;云轴科技ZStack联合飞腾于3月28日正式发布了《教育行业数字化自主创新飞腾生态解决方案白皮书》&#xff08;简称《教育白皮书》&#xff09;。…

Day57:WEB攻防-SSRF服务端请求Gopher伪协议无回显利用黑白盒挖掘业务功能点

目录 SSRF-原理&挖掘&利用&修复 SSRF无回显解决办法 SSRF漏洞挖掘 SSRF协议利用 http:// &#xff08;常用&#xff09; file:/// &#xff08;常用&#xff09; dict:// &#xff08;常用&#xff09; sftp:// ldap:// tftp:// gopher:// &#xff08;…

Python中的全栈开发前端与后端的完美融合【第160篇—全栈开发】

&#x1f47d;发现宝藏 前些天发现了一个巨牛的人工智能学习网站&#xff0c;通俗易懂&#xff0c;风趣幽默&#xff0c;忍不住分享一下给大家。【点击进入巨牛的人工智能学习网站】。 Python中的全栈开发&#xff1a;前端与后端的完美融合 全栈开发已成为当今软件开发领域中的…

YOLOv9有效改进专栏汇总|未来更新卷积、主干、检测头注意力机制、特征融合方式等创新![2024/4/1]

​ 专栏介绍&#xff1a;YOLOv9改进系列 | 包含深度学习最新创新&#xff0c;助力高效涨点&#xff01;&#xff01;&#xff01; 专栏介绍 YOLOv9作为最新的YOLO系列模型&#xff0c;对于做目标检测的同学是必不可少的。本专栏将针对2024年最新推出的YOLOv9检测模型&#xff0…

Java数据结构-栈

目录 1. 栈的概念2. 栈的实现2.1 顺序栈2.2 链式栈 3. 栈的应用3.1 栈的使用3.2 括号匹配3.3 逆波兰表达式求值3.4 出栈入栈次序匹配3.4 最小栈 1. 栈的概念 栈是一种顺序结构&#xff0c;只允许在一端进行插入和删除&#xff0c;插入删除的一端叫栈顶&#xff0c;另一端叫栈底…

蓝桥杯嵌入式学习笔记(7):ADC程序设计

目录 前言 1. ADC原理 1.1 主要特性 1.2 模拟输出电路图 2. 使用CubeMX进行源工程的配置 2.1 引脚配置 2.2 配置AD1 2.3 配置AD2 2.4 配置时钟 3. 代码编程 3.1 预备工作 3.2 bsp_adc.h文件编写 3.3 bsp_adc.c文件编写 3.4 main.c编写 3.4.1 时钟函数配置 3…

内网渗透学习-漏洞利用

1、漏洞搜索与利用 漏洞1&#xff1a;后台弱口令 漏洞2&#xff1a; 在yxcms有个留言功能&#xff0c;常常存在存储型xss漏洞 先用<h1>123xss</h1>测试一下会不会解析 登录管理员账号审核留言&#xff0c;发现xss脚本解析了 写入xss弹窗 再次返回管理员审核页面&…

Hive on Spark 配置

目录 1 Hive 引擎简介2 Hive on Spark 配置2.1 在 Hive 所在节点部署 Spark2.2 在hive中创建spark配置文件2.3 向 HDFS上传Spark纯净版 jar 包2.4 修改hive-site.xml文件2.5 Hive on Spark测试2.6 报错 1 Hive 引擎简介 Hive引擎包括&#xff1a;MR&#xff08;默认&#xff09…

leaflet知识点:leaflet.draw的使用指南

一&#xff0c;安装插件 npm i leaflet-draw --save 二&#xff0c;引入插件 import "leaflet-draw"; import "leaflet-draw/dist/leaflet.draw.css";三&#xff0c;使用插件 leaflet-draw的插件使用有两种方法。 1. 作为工具栏控件加入到地图种使用 //…

linux离线安装NodeJs

一、官方下载 地址&#xff1a;Node.js — Download Node.js 选择linux系统版本 为了防止安装过程出现一些适配问题&#xff0c;我没有选择下载最新版&#xff0c;实际应该下载你的前端所用的nodejs版本 未完待续。。

深度解析Android APP加固中的必备手段——代码混淆技术

Android APP 加固是优化 APK 安全性的一种方法&#xff0c;常见的加固方式有混淆代码、加壳、数据加密、动态加载等。下面介绍一下 Android APP 加固的具体实现方式。 混淆代码 使用 ipaguard工具可以对代码进行混淆&#xff0c;使得反编译出来的代码很难阅读和理解&#xff…

3.5网安学习第三阶段第五周回顾(个人学习记录使用)

本周重点 ①SSRF服务器端请求伪造 ②序列化和反序列化 ③Vaudit代码审计 本周主要内容 ①SSRF服务器端请求伪造 一、概述 SSRF: server site request forgery (服务器端请求伪造)。 SSR: 服务端请求&#xff0c;A服务器通过函数向B服务器发送请求。 SSRF发生的前提条件…

idea 报错 Could not list the contents of folder “ftps

idea 报错 Could not list the contents of folder "ftps 解决方案 这里看到了网上的解决方案&#xff0c;顺便再记录一下。打开 【高级】菜单 - 取消勾选 被动模式。然后点击测试连接&#xff0c;显示连接成功&#xff01; ftp中的主动模式和被动模式 主动模式&…

1.JavaEE进阶篇 - 为什么要学习SpringBoot呢?

文章目录 1.为什么要学框架&#xff1f;2.框架的优点展示(SpringBoot VS Servlet)2.1 Servlet 项⽬开发2.1.1 创建项⽬2.1.2 添加引⽤2.1.3 添加业务代码2.1.4 运⾏项⽬(配置tomcat)2.1.5 Maven配置2.1.5.1修改本地Maven仓库地址2.1.5.2 配置settings.xml文件2.1.5.3项目 本地仓…