本文,我们讲讲前端怎么实现伸缩框的功能,类似下面👇
案例验证的浏览器为 - Google Chrome 版本 119.0.6045.123(正式版本)(arm64)
前言
在实际的工作中,我们有遇到这么一个实用的需求:
允许用户对内容框进行伸缩
咦,这不是很简单的事情?
So EASY~
我们使用 resize
这个 css
样式不就得了。
CSS 中使用 resize
是的,我们可以通过 resize
这个属性来实现伸缩框的功能。比如这样👇我们设定 resize: both;
html
<div class="resize-both">
resize both
</div>
css
:root {
--primary-color: #3498db;
}
.resize-both {
width: 100px;
height: 100px;
border: 1px solid var(--primary-color);
/* resize */
resize: both;
overflow: hidden;
}
resize 需要配合 overflow 属性来使用,属性值为
visible
不生效
实现的效果如下:
当然,我们这里只是以 resize: both;
为案例,我们可以设定其横向或者竖向的伸缩 - mdn web docs - resize。
嗯,resize
确实实现了我们的伸缩框功能。但是,我们是否可以改变下右下角的 icon
图表呢?是否可以更改图标所在的位置呢?这对我们很不友好 -> Is there a way to change the CSS resize corner's position?
So,目前来看,我们只能接受使用 resize
的默认。
那么,我们能否自己来编写伸缩款呢?
能的 ,我们用 javascript
来实现个 resize both
的 gif
图的功能。
JS 实现伸缩框
我们的思路是这样子的:
- 实现右下角的三角拖动图标
- 计算伸缩框距离左边和顶部的距离
- 监听鼠标的点击、拖动、抬起事件,记录鼠标当前相对视窗左上角点的左侧距离和顶部距离
- 计算鼠标距离边框左侧的距离,即边框的新宽度
- 计算鼠标距离边框顶部的距离,即边框的新高度
- 限定边框的最小距离,防止
icon
拖动隐藏
我们需要跟浏览器的事件打交道,这里引入 RxJS ,(当然,读者可以手写原生 javascript
)。
RxJS
是一个用于处理异步事件流的库。
在开始之前,我们还得熟悉下juejin.cn/post/708512...中的 Element.getBoundingClientRect() 方法:
我们可以通过这个方法获取元素其左上角顶点相对可视窗口的坐标(x, y)
及其元素的宽度和高度。
当然,我们还需要通过 event.clienX
和 event.clientY
获取当前鼠标距离可视窗口的坐标(clientX, clientY)
。
Ok,万事俱备,我们来实现下:
html
<div class="rxjs-both" id="rxjs-both">
rxjs both
<div class="icon-resize" id="icon-resize"></div>
</div>
类名为 icon-resize
的元素是用来实现右下角的三角图标的,这里我们结合 css
中的伪元素来实现:
css
:root {
--primary-color: #3498db;
--icon-color: #666;
}
.rxjs-both {
width: 100px;
height: 100px;
border: 1px solid var(--primary-color);
margin-left: 400px;
position: relative;
overflow: hidden;
}
.icon-resize {
width: 10px;
height: 10px;
position: absolute;
bottom: 0;
right: 0;
/* 更改了 cusor 图标 */
cursor: nwse-resize;
}
.icon-resize::before {
content: "";
display: block;
width: 10px;
height: 1px;
background-color: var(--icon-color);
position: absolute;
top: 50%;
transform: translateY(-50%) rotate(135deg);
}
.icon-resize::after {
content: "";
display: block;
width: 5px;
height: 1px;
background-color: var(--icon-color);
position: absolute;
top: 75%;
left: 75%;
transform: translate(-50%, -50%) rotate(135deg);
}
页面的效果如下:
接着,我们添加 javascript
让页面动起来:
javascript
const { fromEvent } = rxjs;
const { mergeMap, takeUntil } = rxjs.operators;
const resizableDiv = document.getElementById('rxjs-both');
function handleMouseMove(event) {
let boundingEl = resizableDiv.getBoundingClientRect();
let _width = event.clientX - boundingEl.left; // 获取拖动后的宽度
let _height = event.clientY - boundingEl.top; // 获取拖动后的高度
resizableDiv.style.width = (_width >= 20 ? _width : 20) + 'px';
resizableDiv.style.height = (_height >= 20 ? _height : 20) + 'px';
}
const mouseMove$ = fromEvent(document, 'mousemove'); // fromEvent 创建可观察对象 Observable
const mouseUp$ = fromEvent(document, 'mouseup');
const drag$ = fromEvent(document.getElementById('icon-resize'), 'mousedown').pipe(
mergeMap(() => mouseMove$.pipe(takeUntil(mouseUp$))) // 鼠标抬起结束监听
);
drag$.subscribe(handleMouseMove); // 观察者 -> 鼠标拖动的过程中监听
这里我们引入的 rxjs
为 7.8.1
版本,读者可以直接使用 cdn -> https://cdn.bootcdn.net/ajax/libs/rxjs/7.8.1/rxjs.umd.js
。当鼠标按下拖动的时候,触发对伸缩框的宽度和高度的重新计算并赋值。当鼠标抬起后,结束监听。
实现的效果可以说和 CSS
实现的 resize: both
的大同小异,优雅且丝滑~ 如下:
是的,这里我们实现了拉取右下角的图标实现对伸缩框的高度和宽度做了更改。其实,是否更改宽度或者高度,是否更改图标的位置,或者是否通过拉取边框进行伸缩?原理都一样,读者感兴趣的话,可以自行实现~
总结
我们实验了两种的伸缩方法:
CSS
中resize
结合overflow
来实现,虽然能够完成任务,但是太固化,适合不讲究页面布局的时候实现JS
方式实现,引入了RxJS
优雅实现,可对UI
高度定制化