LOGO OA教程 ERP教程 模切知识交流 PMS教程 CRM教程 开发文档 其他文档  
 
网站管理员

使用 HTML + JavaScript 实现 IDE 风格的拖拽调整布局功能(附源码地址)

admin
2026年4月13日 12:29 本文热度 109
在编程开发中,如何高效地利用屏幕空间始终是一个核心议题。一个支持动态调整的布局系统能够让用户根据当前任务的优先级,自由分配各个功能区域的显示比例。这种高度可定制的交互体验不仅提升了操作的流畅度,更在视觉上营造出一种专业且沉浸的开发环境。本文将介绍如何使用 HTML、CSS 和 JavaScript 实现 IDE 风格的拖拽调整布局功能

效果演示

打开页面后,用户将看到一个模拟集成开发环境(IDE)的深色主题界面。整个工作区被划分为三个主要的垂直区域:左侧的资源管理器、中间的代码编辑器与调试器组合区,以及右侧的移动端模拟器。系统主要包含以下交互行为左右宽度调整、上下高度调整、边界限制、窗口自适应等。通过这些交互,用户可以快速定制属于自己的工作台布局,让编码、预览、调试各区域各得其所。

页面结构

页面主要包括顶部工具栏、左侧资源管理器、中间工作区(含编辑器与控制台)、右侧模拟器以及用于控制尺寸的分割条。

顶部工具栏

工具栏位于页面最顶端,提供基础的菜单入口,采用 Flex 布局固定在顶部。
<div class="toolbar">  <div class="menu">    <div>文件</div>    <div>编辑</div>    <div>视图</div>    <div>调试</div>  </div></div>

左侧资源管理器

资源管理器区域用于展示项目文件树结构,具有固定的初始宽度,支持内容滚动。
<div class="left-panel" id="leftPanel">  <div class="panel-header">资源管理器</div>  <div class="panel-content">    <ul class="file-tree">      <!-- 文件列表项 -->    </ul>  </div></div>

中间工作区

工作区占据页面的主要空间,内部又分为上方的代码编辑区和下方的调试控制台,两者之间通过水平分割条隔开。
<div class="middle-panel" id="middlePanel">  <div class="middle-top" id="middleTop">    <div class="panel-header">App.vue</div>    <textarea class="panel-content code-editor" id="codeEditor"></textarea>  </div>  <div class="resizer-horizontal" id="middleResizer"></div>  <div class="middle-bottom" id="middleBottom">    <div class="panel-header">调试器</div>    <div class="panel-content console-log" id="consoleContent"></div>  </div></div>

右侧模拟器

模拟器区域用于展示移动端预览效果,具有独立的背景色与样式。
<div class="right-panel" id="rightPanel">  <div class="panel-header">模拟器</div>  <div class="panel-content" style="padding: 8px;">    <div class="simulator-container">      <!-- 模拟器内容 -->    </div>  </div></div>

垂直分割条

垂直分割条分布在面板之间,作为拖拽的触发点。
<div class="resizer-vertical" id="leftResizer"></div><div class="resizer-vertical" id="rightResizer"></div>

核心功能实现

拖拽调整布局的核心在于:监听分割条上的鼠标事件,动态计算并更新相邻面板的尺寸,同时施加严格的边界限制,避免某一侧面板被过度压缩或超出可视范围。在样式定义中,.workspace 占据了除工具栏外的所有剩余空间。内部的 .top-area 再次使用横向 Flex 布局,将左、中、右三个面板串联起来。分割条被赋予了特定的 cursor 样式(col-resize 或 row-resize),并在悬停或激活状态下改变背景色,为用户提供明确的视觉反馈。

左侧面板宽度拖拽逻辑

首先,我们需要定义一些常量来约束拖拽的范围,例如 MIN_LEFT_WIDTH(最小宽度)和 MAX_LEFT_RATIO(最大宽度占父容器的比例)。这能防止用户将面板拖得太小导致内容无法阅读,或者拖得太大挤占其他区域的空间。
当用户在 leftResizer 上按下鼠标时,系统会记录当前的鼠标横坐标 startXLeft 以及左侧面板的初始宽度 startLeftWidth在 mousemove 事件中,我们通过计算鼠标当前位置与起始位置的差值 deltaX,得出新的宽度值。但直接赋值是不够的,我们必须进行边界校验。这里有一个关键的计算逻辑:新宽度不能超过“父容器总宽度减去右侧面板宽度、分割条宽度以及中间面板最小保留宽度”后的剩余空间。
let isDraggingLeft = false;let startXLeft = 0;let startLeftWidth = 0;
leftResizer.addEventListener('mousedown'(e) => {  e.preventDefault();  isDraggingLeft = true;  startXLeft = e.clientX;  startLeftWidth = leftPanel.getBoundingClientRect().width;  document.body.style.cursor = 'col-resize';  leftResizer.classList.add('active');  document.body.style.userSelect = 'none';  document.addEventListener('mousemove', onMouseMoveLeft);  document.addEventListener('mouseup', onMouseUpLeft);});
function onMouseMoveLeft(e) {  if (!isDraggingLeft) return;  const deltaX = e.clientX - startXLeft;  let newWidth = startLeftWidth + deltaX;  const parentWidth = getParentWidth();  const rightWidth = rightPanel.getBoundingClientRect().width;  const resizersTotalWidth = 8;  const minMiddleWidth = 120;  const maxLeftByLayout = parentWidth - rightWidth - resizersTotalWidth - minMiddleWidth;  const maxLeftByRatio = parentWidth * MAX_LEFT_RATIO;  const maxAllowed = Math.min(maxLeftByLayout, maxLeftByRatio);  newWidth = Math.max(MIN_LEFT_WIDTHMath.min(newWidth, maxAllowed));  if (newWidth >= MIN_LEFT_WIDTH && newWidth <= maxAllowed) {    leftPanel.style.width = newWidth + 'px';  }}
function onMouseUpLeft() {  isDraggingLeft = false;  document.body.style.cursor = '';  leftResizer.classList.remove('active');  document.body.style.userSelect = '';  document.removeEventListener('mousemove', onMouseMoveLeft);  document.removeEventListener('mouseup', onMouseUpLeft);}

右侧面板宽度拖拽逻辑

右侧面板的拖拽逻辑与左侧类似,但方向相反。当用户向左拖动右侧分割条时,右侧面板的宽度应当增加;向右拖动时,宽度减小。因此,在计算 deltaX 时,我们使用 startXRight - e.clientX

同样地,我们需要确保右侧面板变宽时,不会导致中间面板的宽度小于预设的最小值 minMiddleWidth。这种相互制约的逻辑保证了布局的健壮性。

let isDraggingRight = false;let startXRight = 0;let startRightWidth = 0;
rightResizer.addEventListener('mousedown'(e) => {  e.preventDefault();  isDraggingRight = true;  startXRight = e.clientX;  startRightWidth = rightPanel.getBoundingClientRect().width;  document.body.style.cursor = 'col-resize';  rightResizer.classList.add('active');  document.body.style.userSelect = 'none';  document.addEventListener('mousemove', onMouseMoveRight);  document.addEventListener('mouseup', onMouseUpRight);});
function onMouseMoveRight(e) {  if (!isDraggingRight) return;  const deltaX = startXRight - e.clientX;  let newWidth = startRightWidth + deltaX;  const parentWidth = getParentWidth();  const leftWidth = leftPanel.getBoundingClientRect().width;  const resizersTotalWidth = 8;  const minMiddleWidth = 120;  const maxRightByLayout = parentWidth - leftWidth - resizersTotalWidth - minMiddleWidth;  const maxRightByRatio = parentWidth * MAX_RIGHT_RATIO;  const maxAllowed = Math.min(maxRightByLayout, maxRightByRatio);  newWidth = Math.max(MIN_RIGHT_WIDTHMath.min(newWidth, maxAllowed));  if (newWidth >= MIN_RIGHT_WIDTH && newWidth <= maxAllowed) {    rightPanel.style.width = newWidth + 'px';  }}

中间区域高度拖拽逻辑

中间区域的高度调整涉及纵向布局。与宽度调整不同,这里我们关注的是 middleBottom(调试器)的高度变化。当用户向下拖动水平分割条时,调试器的高度增加,编辑器的高度相应减小。

在 onMouseMoveMiddleBottom 函数中,我们计算垂直方向的位移 deltaY。限制条件包括:调试器的最小高度 MIN_MIDDLE_BOTTOM_HEIGHT,以及为了保证编辑器至少有 minMiddleTopHeight 的空间,调试器最大不能超过 middleHeight - minMiddleTopHeight

let isDraggingMiddleBottom = false;let startYMiddleBottom = 0;let startMiddleBottomHeight = 0;
middleResizer.addEventListener('mousedown'(e) => {  e.preventDefault();  isDraggingMiddleBottom = true;  startYMiddleBottom = e.clientY;  startMiddleBottomHeight = middleBottom.getBoundingClientRect().height;  document.body.style.cursor = 'row-resize';  middleResizer.classList.add('active');  document.body.style.userSelect = 'none';  document.addEventListener('mousemove', onMouseMoveMiddleBottom);  document.addEventListener('mouseup', onMouseUpMiddleBottom);});
function onMouseMoveMiddleBottom(e) {  if (!isDraggingMiddleBottom) return;  const deltaY = startYMiddleBottom - e.clientY;  let newHeight = startMiddleBottomHeight + deltaY;  const middleHeight = getMiddleHeight();  const minMiddleTopHeight = 150;  const maxMiddleBottomHeight = middleHeight - minMiddleTopHeight;  const maxByRatio = middleHeight * MAX_MIDDLE_BOTTOM_RATIO;  const maxAllowed = Math.min(maxMiddleBottomHeight, maxByRatio);  newHeight = Math.max(MIN_MIDDLE_BOTTOM_HEIGHTMath.min(newHeight, maxAllowed));  if (newHeight >= MIN_MIDDLE_BOTTOM_HEIGHT && newHeight <= maxAllowed) {    middleBottom.style.height = newHeight + 'px';  }}

窗口 resize 时的边界补偿

在实际使用中,用户可能会缩小浏览器窗口。如果窗口缩小的幅度很大,原本合理的左右面板宽度之和可能会超过新的窗口宽度,导致中间面板被完全挤压消失。为了解决这个问题,我们监听了 window 的 resize 事件。

在 adjustLayoutOnResize 函数中,我们重新计算中间面板的理论宽度。如果发现其小于最小阈值 MIN_MIDDLE,程序会自动从右侧面板(优先)或左侧面板中扣除相应的宽度,以“保护”中间核心工作区的可见性。这种防御性编程极大地提升了用户体验。

function adjustLayoutOnResize() {  const parentWidth = getParentWidth();  const leftWidth = leftPanel.getBoundingClientRect().width;  const rightWidth = rightPanel.getBoundingClientRect().width;  const resizersWidth = 8;  const middleWidth = parentWidth - leftWidth - rightWidth - resizersWidth;  const MIN_MIDDLE = 120;  if (middleWidth < MIN_MIDDLE) {    const shortage = MIN_MIDDLE - middleWidth;    let newRightWidth = rightWidth - shortage;    const minRight = MIN_RIGHT_WIDTH;    if (newRightWidth >= minRight) {      rightPanel.style.width = newRightWidth + 'px';    } else {      let newLeftWidth = leftWidth - (shortage - (rightWidth - minRight));      newLeftWidth = Math.max(MIN_LEFT_WIDTH, newLeftWidth);      leftPanel.style.width = newLeftWidth + 'px';      rightPanel.style.width = minRight + 'px';    }  }  const maxLeft = parentWidth * MAX_LEFT_RATIO;  if (leftWidth > maxLeft) {    leftPanel.style.width = maxLeft + 'px';  }  const maxRight = parentWidth * MAX_RIGHT_RATIO;  if (rightWidth > maxRight) {    rightPanel.style.width = maxRight + 'px';  }}// 处理resize事件let resizeTimer;window.addEventListener('resize'() => {  if (resizeTimer) clearTimeout(resizeTimer);  resizeTimer = setTimeout(() => {    adjustLayoutOnResize();    const currentMiddleBottomH = middleBottom.getBoundingClientRect().height;    const middleH = getMiddleHeight();    const maxMiddleBottom = middleH - 150;    const maxByRatio = middleH * MAX_MIDDLE_BOTTOM_RATIO;    const maxAllowed = Math.min(maxMiddleBottom, maxByRatio);    if (currentMiddleBottomH > maxAllowed && maxAllowed >= MIN_MIDDLE_BOTTOM_HEIGHT) {      middleBottom.style.height = maxAllowed + 'px';    }    if (currentMiddleBottomH < MIN_MIDDLE_BOTTOM_HEIGHT) {      middleBottom.style.height = MIN_MIDDLE_BOTTOM_HEIGHT + 'px';    }  }, 100);});

源码地址

git 地址:https://gitee.com/ironpro/hjdemo/blob/master/drag-layout/index.html


阅读原文:https://mp.weixin.qq.com/s/2Zgs6EzqTKGNVCmogG7ZbQ


该文章在 2026/4/13 12:29:40 编辑过
关键字查询
相关文章
正在查询...
点晴ERP是一款针对中小制造业的专业生产管理软件系统,系统成熟度和易用性得到了国内大量中小企业的青睐。
点晴PMS码头管理系统主要针对港口码头集装箱与散货日常运作、调度、堆场、车队、财务费用、相关报表等业务管理,结合码头的业务特点,围绕调度、堆场作业而开发的。集技术的先进性、管理的有效性于一体,是物流码头及其他港口类企业的高效ERP管理信息系统。
点晴WMS仓储管理系统提供了货物产品管理,销售管理,采购管理,仓储管理,仓库管理,保质期管理,货位管理,库位管理,生产管理,WMS管理系统,标签打印,条形码,二维码管理,批号管理软件。
点晴免费OA是一款软件和通用服务都免费,不限功能、不限时间、不限用户的免费OA协同办公管理系统。
Copyright 2010-2026 ClickSun All Rights Reserved  粤ICP备13012886号-9  粤公网安备44030602007207号