0
点赞
收藏
分享

微信扫一扫

创建可调整大小的拆分视图

刘员外__ 2022-07-27 阅读 59

创建可调整大小的拆分视图

在这篇文章中,我们将添加一个元素来调整给定元素的子元素的大小。基本结构如下:

<div style="display: flex">
<div>Left</div>
<div class="resizer" id="dragMe"></div>
<div>Right</div>
</div>

为了将 ​​left​​​、​​resizer​​​ 和 ​​right​​​ 元素放在同一行,我们将 ​​display: flex​​ 样式添加到父级。

拖动 ​​resizer​​ 元素时更新左侧的宽度

在我们的例子中,可以水平拖动 ​​resizer​​。首先,当用户开始单击调整器时,我们必须存储鼠标位置和左侧的宽度:

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

// 鼠标的当前位置
let x = 0
let y = 0

// 左侧宽度
let leftWidth = 0

// 处理当用户拖动大小调整器时触发的 mousedown 事件
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)

从标签的结构来看,左侧和右侧是 ​​resizer​​ 的上一个兄弟和下一个兄弟。它们可以被检索如上所述:

const leftSide = resizer.previousElementSibling
const rightSide = resizer.nextElementSibling

接下来,当用户移动鼠标时,我们确定鼠标移动了多远,然后更新左侧的宽度:

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}%`
}

这里需要注意两点:

  • 左侧的宽度是根据父级宽度的百分比数设置的。它保持了左侧和侧边宽度的比例,并在用户调整浏览器大小时使两侧看起来很好。
  • 如果我们总是强制其采用剩余宽度,则无需更新右侧的宽度:

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

修复闪烁的问题

当用户移动大小调整器时,我们应更新其光标:

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

但它引发了另一个问题。只要用户移动鼠标,我们就会看到默认的鼠标光标,因为鼠标不在大小调整器的顶部。用户将看到屏幕闪烁,因为光标不断变化。

为了解决这个问题,我们为整个页面设置了光标:

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

我们还设置 ​​user-select​​​ 和 ​​pointer-events​​​ 为 ​​none​​,以防止防止两侧的鼠标事件和文本选择:

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

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

这些样式在用户停止移动鼠标后立即移除:

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')

// 移除 mousemove 和 mouseup 的处理程序
document.removeEventListener('mousemove', mouseMoveHandler)
document.removeEventListener('mouseup', mouseUpHandler)
}

支持垂直方向

很容易支持垂直拆分侧面。我们现在更新顶部的高度,而不是更新左侧的宽度:

const prevSibling = resizer.previousElementSibling
let prevSiblingHeight = 0

const mouseDownHandler = function (e) {
const rect = prevSibling.getBoundingClientRect()
prevSiblingHeight = rect.height
}

const mouseMoveHandler = function (e) {
const h =
((prevSiblingHeight + dy) * 100) /
resizer.parentNode.getBoundingClientRect().height
prevSibling.style.height = `${h}%`
}

当用户移动大小调整器元素时,我们也会更改光标:

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

支持两个方向

假设右侧希望拆分为两个可调整大小的元素。

我们目前有两个 ​​resizer​​​ 元素。为了指示每个缩放器的拆分方向,我们添加了一个自定义属性 ​​data-direction​​:

<div style="display: flex">
<div>Left</div>
<div class="resizer" data-direction="horizontal"></div>
<div style="display: flex; flex: 1 1 0%; flex-direction: column">
<div>Top</div>
<div class="resizer" data-direction="vertical"></div>
<div style="flex: 1 1 0%">Bottom</div>
</div>
</div>

稍后,我们可以从 ​​resizer​​ 元素中检索属性:

const direction = resizer.getAttribute('data-direction') || 'horizontal'

设置前一个兄弟的宽度或高度的逻辑取决于方向:

const mouseMoveHandler = function (e) {
switch (direction) {
case 'vertical':
const h =
((prevSiblingHeight + dy) * 100) /
resizer.parentNode.getBoundingClientRect().height
prevSibling.style.height = `${h}%`
break
case 'horizontal':
default:
const w =
((prevSiblingWidth + dx) * 100) /
resizer.parentNode.getBoundingClientRect().width
prevSibling.style.width = `${w}%`
break
}

const cursor = direction === 'horizontal' ? 'col-resize' : 'row-resize'
resizer.style.cursor = cursor
document.body.style.cursor = cursor

// ...
}

​​查看效果​​

举报

相关推荐

0 条评论