likes
comments
collection
share

如何创建可调整大小的拆分视图,类似 Gitlab Code Review 界面

作者站长头像
站长
· 阅读数 26

这篇文章介绍如何创建一个可调整大小的拆分视图,什么意思呢?请看如下示例。

如上例所示,拖地中间的竖条,就可以改变视图的大小。

实现方案

先定义好 HTML 结构。

<div style="display: flex">
  <!-- 左侧视图元素 -->
  <div>Left</div>

  <!-- 拖动条 -->
  <div class="resizer" id="dragMe"></div>

  <!-- 右侧元素 -->
  <div>Right</div>
</div>

定义一个容器元素,使用 flex 布局,让所有子元素都在同一行展示。定义三个子元素,中间元素为拖动条,左右两边为视图。

实现拖动

拖动元素的方式在之前的文章 如何实现一个自定义的 range slider?元素拖动其实很简单 有描述过,有兴趣的 JYM 可以看看。

我们需要记录可拖动元素的初始位置以及拖动的距离,所以需要使用到 mousedown 事件。

const resizer = document.getElementById('dragMe');
const leftSide = resizer.previousElementSibling;
const rightSide = resizer.nextElementSibling;

// 鼠标默认位置
let x = 0;
let y = 0;

// 左侧元素的宽度
let leftWidth = 0;

const mouseDownHandler = function (e) {
  // 记录鼠标位置
  x = e.clientX;
  y = e.clientY;

  // 记录左侧元素的宽度
  leftWidth = leftSide.getBoundingClientRect().width;

  // 在 document 元素上添加事件
  document.addEventListener('mousemove', mouseMoveHandler);
  document.addEventListener('mouseup', mouseUpHandler);
};

resizer.addEventListener('mousedown', mouseDownHandler);

注意是在 document 元素上监听 mousemovemouseup 事件。

查找一个元素的兄弟元素,我们可以使用 previousElementSiblingnextElementSibling 属性。

除了记录鼠标的位置,还需要记录左侧元素的宽度,方便后面进行计算。

记下来,当我们拖动元素,我们需要实时计算出鼠标移动的距离,然后更新左侧元素的宽度。

const mouseMoveHandler = function (e) {
  // 计算圆度拖动距离
  const dx = e.clientX - x;
  const dy = e.clientY - y;

  // 计算左侧元素的宽度值
  const newLeftWidth =
    ((leftWidth + dx) * 100) / resizer.parentNode.getBoundingClientRect().width;
  leftSide.style.width = `${newLeftWidth}%`;
};

左侧元素的宽度使用百分比值,而不是绝对像素值。

因为我们给父级容器元素设置了 flex 布局,所以我们可以很轻松的让右侧元素自适应宽度。

<div style="display: flex">
  <!-- Left element -->
  ...

  <!-- The resizer -->
  ...

  <!-- Right element -->
  <div style="flex: 1 1 0%;">Right</div>
</div>

如上示例,使用 flex: 1 1 0%; 即可让右侧元素跟随左侧宽度自适应。

一些优化

当我们移动鼠标的时候,我们需要更新鼠标的展现形式。

const mouseMoveHandler = function (e) {
  // ...
  resizer.style.cursor = 'col-resize';
};

但是这样做会导致一个问题,当我们移动鼠标的时候,我们会看到鼠标的展示形式在闪烁。这是因为鼠标在拖动的过程中离开了 .resizer 元素,然后又回到此元素上,反复如此。

所以我们需要在鼠标拖动的时候,给 document 元素也加上鼠标展示样式。

const mouseMoveHandler = function (e) {
  // ...
  document.body.style.cursor = 'col-resize';
};

然后在拖动过程中,我们还需要阻止内容的选中。

const mouseMoveHandler = function (e) {
  // ...
  leftSide.style.userSelect = 'none';
  leftSide.style.pointerEvents = 'none';

  rightSide.style.userSelect = 'none';
  rightSide.style.pointerEvents = 'none';
};

使用 userSelectpointerEvents 属性可以做到这点,我们需要在左右两侧的元素上都加上这个样式属性。

然后,当松开鼠标时,也就是 mouseup 事件中,将上述优化给移还原。

const mouseUpHandler = function () {
  resizer.style.removeProperty('cursor');
  document.body.style.removeProperty('cursor');

  leftSide.style.removeProperty('user-select');
  leftSide.style.removeProperty('pointer-events');

  rightSide.style.removeProperty('user-select');
  rightSide.style.removeProperty('pointer-events');

  document.removeEventListener('mousemove', mouseMoveHandler);
  document.removeEventListener('mouseup', mouseUpHandler);
};

全文完,如果觉得这篇文章对你有用,欢迎 点赞 👍、评论 ✍️、收藏 👀

转载自:https://juejin.cn/post/7134720399119482916
评论
请登录