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

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.hqwc.cn/news/586115.html

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

相关文章

数据类型和类型检测

Data Type And Type Checking 1.编程语言中的数据类型 类型和变量 一个类型是一系列值的集合&#xff0c;这些集合可以抽象出一个相同的特点&#xff0c;并且可以相互实现计算 例如&#xff1a; 布尔类型&#xff1a;true or false整形&#xff1a;1,2,3…浮点数类型&#xf…

JUC:double-checked locking(DCL) 懒汉单例模式

文章目录 double-checked locking(DCL) 问题解决方法 volatile作用 double-checked locking(DCL) 问题 第一个if用于后续进入的线程&#xff0c;不用再获取锁来判断是否已经创建了对象。第二个if&#xff0c;为的是第一个进入的线程创建对象&#xff0c;以及防止卡在第一个if之…

提升你的CSS技能:深入理解伪类选择器和伪元素选择器!

在CSS的世界里&#xff0c;有些选择器并不像它们的名字那样直接。今天&#xff0c;我们要探索的是两种特殊的选择器&#xff1a;伪类选择器和伪元素选择器。它们虽然名字相似&#xff0c;但功能和用途却大有不同。 下面就让我们一起来了解一下它们是如何在我们的页面布局中扮演…

GoogleNet神经网络介绍

一、简介 GoogleNet&#xff0c;也称为GoogLeNet&#xff0c;是谷歌工程师设计的一种深度神经网络结构&#xff0c;它在2014年的ImageNet图像识别挑战赛中取得了冠军。该神经网络的设计特点主要体现在其深度和宽度上&#xff0c;通过引入名为Inception的核心子网络结构&#x…

vue项目视频播放ckplayer使用

ckplayer 官方网址&#xff0c;点击访问 1&#xff0c;打开网页后能看到这里&#xff0c;我现在使用的是最新 X3版手册 2&#xff0c;这个ckplayer不是npm 插件&#xff0c;要下载安装包解压到项目里面使用 安装包网址 通过gitee下载 3&#xff0c;解析安装包到项目中 publ…

护眼台灯哪个牌子最好,护眼台灯五大品牌墙裂分享

近视在儿童中愈发普遍&#xff0c;许多家长开始认识到&#xff0c;除了学业成绩之外&#xff0c;孩子的视力健康同样重要。毕竟&#xff0c;学业的落后可以逐渐弥补&#xff0c;而一旦孩子近视&#xff0c;眼镜便可能成为长期伴随。因此&#xff0c;专业的护眼台灯对于每个家庭…

【zlm】音视频流与音频流合并的设计

目录 设想一 设想二 方案三 关键技术 测试语句 测试脚本 参考文档 设想一 //开始录制_option.mp4_save_path custom_path;_option.mp4_max_second max_second;vector<Track::Ptr> mytracks getTracks();auto src MediaSource::find( DEFAULT_VHOST, "1&quo…

今天起,ChatGPT无需注册就能用了!

就在刚刚&#xff0c;OpenAI狠狠地open了一把&#xff1a; 从今天起&#xff0c;ChatGPT打开即用&#xff0c;无需再注册帐号和登录了&#xff01; 像这样&#xff0c;直接登录网站&#xff0c;然后就可以开启对话&#xff1a; OpenAI对发布这个“超自由版ChatGPT”的解释是&am…

将 EDI 从 VAN 迁移到知行之桥:EDI 成本降低 90%

G公司帮助零售商在网上和实体店取得成功&#xff0c;是来自300家顶级制造商网络中包含狩猎&#xff0c;钓鱼&#xff0c;露营和体育用品等45,000 多种商品的批发分销商。 通过可靠的客户服务、良好的定价和超越标准大卖场产品的库存量&#xff0c;G公司的重点是为零售商、电子…

JavaScript邂逅

文章目录 Javascript内容邂逅JavaScript前端的三大核心计算机语言认识编程语言常见的编程语言编程语言的发展历史–机器语言阶段一: 机器语言 编程语言的发展历史–汇编语言阶段二:汇编语言 汇编语言的发展历史–高级语言阶段三:高级语言 机器语言和高级语言 认识JavaScriptJav…

Vmware虚拟机远程SSH连接失效解决方法及分析过程

问题描述 系统为CentOS7 64位 一向能够正常连接 在某次使用FinalShell进行远程SSH连接时 突然无法连接 对问题原因猜测及尝试 1.dns解析失效 发现显示DNS无法解析,可以得知是使用了主机名登录,而主机名无法登录. 遂查看host文件.host文件因不知名原因被清空,重新恢复后该问…

初识编译和链接(C语言)

文章目录 编译和链接翻译环境预处理编译汇编链接 运行环境 编译和链接 编译和链接这两个大的过程构成了翻译环境。 其实&#xff0c;在ANSI C的任何一种实现中&#xff0c;存在两个不同的环境。 一个环境是翻译环境&#xff0c;另一个是执行环境。 翻译环境中&#xff0c;源…