最近在进行开发时,遇到需要动态拉升左侧区域,从而改变左右两栏尺寸的布局需求。这是一个很常见的场景,通过 DOM 操作来实现是第一个想到的方法。仔细查找资料,在大神张鑫旭的博客上发现还有一个性能更好,利用纯 CSS 就能实现分栏宽度拉伸调整的效果。下面的代码和部分内容引用自该博客:
首先要实现的效果,拖拽分隔线,动态改变左右两栏尺寸。
一、HTML 代码:
<!--HTML 代码-->
<div class = "column">
<div class = "column-left">
<div class="resize-bar"> </div>
<div class="resize-line"> </div>
<div class="resize-content"> 左侧的内容,左侧的内容,左侧的内容,左侧的内容 </div>
</div>
<div class = "column-right">
右侧的内容,右侧的内容,右侧的内容,右侧的内容,右侧的内容,右侧的内容
</div>
</div>
二、CSS 代码
/* CSS 代码*/
.column {
overflow: hidden;
}
.column-left {
height: 400px; /*此处高度不能设置100%,否则无法拖动,原因是100%无 scrollbar*/
background-color: #fff;
position: relative;
float: left;
}
.column-right {
height: 400px;
padding: 16px;
background-color: #eee;
box-sizing: border-box;
overflow: hidden;
}
.resize-content {
position: absolute;
top: 0; right:5px; bottom: 0 ; left: 0;
padding: 16px;
overflow-x: hidden;
}
.resize-bar {
width: 200px; height: inherit;
resize: horizontal;
cursor: ew-resize;
cursor: col-resize;
opacity: 0;
overflow: scroll;
}
/*拖拽线*/
.resize-line {
position: absolute;
right: 0; top: 0; bottom: 0;
border-right: 2px solid #eee;
border-left: 1px solid #bbb;
pointer-events: none;
}
.resize-bar:hover ~ .resize-line, .resize-bar:active ~ .resize-line {
border-left: 1px dashed skyblue;
}
.resize-bar::-webkit-scrollbar {
width: 200px; height: inherit;
}
/*Firefox 只有下面一小块区域可以拉伸*/
@supports (-moz-user-select: none) {
.resize-bar:hover ~ .resize-line, .resize-bar:active ~ .resize-line {
border-left: 1px solid #bbb;
}
.resize-bar:hover ~ .resize-line::after, .resize-bar:active ~ .resize-line::after {
content: ' ';
position: absolute;
width: 16px; height: 16px;
bottom: 0; right: -8px;
background: none;
background-size: 100% 100%;
}
}
~ 表示兄弟组合器,类似于相邻的兄弟组合器。区别在于,第二个选择器不必紧跟第一个选择器,这意味着它将选择第一个选择器后面的所有元素。
div ~ p { background-color: blue;}
<div id = "container">
<p>First </p> <!--nothing-->
<div><p>Child Paragraph</p> </div><!--nothing-->
<p> Second </p> <!--background-color: blue-->
<p> Third </p> <!--background-color: blue-->
</div>
三、原理
利用浏览器非 overflow: auto 的属性,设置元素 resize 可以拉伸的特性,从而实现纯 CSS 的分栏宽度控制。
webkit 浏览器下滚动条可以自定义,其中 resize 区域大小就是 scrollbar 的大小,于是,我们就可以将整个拉伸区域变成和容器一样高。
四、属性 resize 参数
resize 属性主要是用来改变元素尺寸大小的,其主要目的是增强用户体验。
resize: none | both | horizontal | vertical | inherit
在 CSS3 中 resize 属性指定的值分为以下几种。
- none: 用户不能拖动元素修改尺寸大小。
- both: 用户可以拖动元素,同时修改元素的高度和宽度。
- horizontal: 用户可以拖动元素,仅可以改变元素的宽度,但不能修改元素的高度;
- vertical: 用户可以拖动元素,仅可以改变元素的高度,但不能修改元素的宽度;
- inherit: 继承父元素的 resize 属性值。
五、利用组件 react-resizable
1、安装 react-resizable
npm install --save react-resizable
2、使用 react-resizable, demo 如下
import React from 'react';
const Resizable = require('react-resizable').Resizable; // or,
const ResizableBox = require('react-resizable').ResizableBox;
// ES6
import { Resizable, ResizableBox } from 'react-resizable';
export default class TestLayout extends React.Component {
state = {width: 200, height: 200};
onClick = () => {
this.setState({width: 200, height: 200});
};
onResize = (event, {element, size}) => {
this.setState({width: size.width, height: size.height});
};
render() {
return (
<div>
<button onClick={this.onClick} style={{'marginBottom': '10px'}}>Reset first element's width/height</button>
<div className="layoutRoot">
<Resizable className="box" height={this.state.height} width={this.state.width} onResize={this.onResize}>
<div className="box" style={{width: this.state.width + 'px', height: this.state.height + 'px'}}>
<span className="text">{"Raw use of <Resizable> element. 200x200, no constraints."}</span>
</div>
</Resizable>
<ResizableBox className="box" width={200} height={200}>
<span className="text">{"<ResizableBox>, same as above."}</span>
</ResizableBox>
<ResizableBox className="box" width={200} height={200} draggableOpts={{grid: [25, 25]}}>
<span className="text">Resizable box that snaps to even intervals of 25px.</span>
</ResizableBox>
<ResizableBox className="box" width={200} height={200} minConstraints={[150, 150]} maxConstraints={[500, 300]}>
<span className="text">Resizable box, starting at 200x200. Min size is 150x150, max is 500x300.</span>
</ResizableBox>
<ResizableBox className="box box3" width={200} height={200} minConstraints={[150, 150]} maxConstraints={[500, 300]}>
<span className="text">Resizable box with a handle that only appears on hover.</span>
</ResizableBox>
<ResizableBox className="box" width={200} height={200} lockAspectRatio={true}>
<span className="text">Resizable square with a locked aspect ratio.</span>
</ResizableBox>
<ResizableBox className="box" width={200} height={120} lockAspectRatio={true}>
<span className="text">Resizable rectangle with a locked aspect ratio.</span>
</ResizableBox>
<ResizableBox className="box" width={200} height={200} axis="x">
<span className="text">Only resizable by "x" axis.</span>
</ResizableBox>
<ResizableBox className="box" width={200} height={200} axis="y">
<span className="text">Only resizable by "y" axis.</span>
</ResizableBox>
<ResizableBox className="box" width={200} height={200} axis="both">
<span className="text">Resizable ("both" axis).</span>
</ResizableBox>
<ResizableBox className="box" width={200} height={200} axis="none">
<span className="text">Not resizable ("none" axis).</span>
</ResizableBox>
</div>
</div>
);
}
}
/*styles.css*/
.react-resizable {
position: relative;
height: 100%;
float: left;
}
.react-resizable-handle {
position: absolute;
width: 1px;
height: 100%;
bottom: 0;
right: 0;
background: #c0c7d0;
padding: 0;
background-repeat: repeat;
background-origin: content-box;
box-sizing: border-box;
cursor: col-resize;
top: 0 !important;
}
3、<Resizable>
和<ResizableBox>
的参数
{
children: React.Element<any>,
width: number,
height: number,
// If you change this, be sure to update your css
handleSize: [number, number] = [10, 10],
lockAspectRatio: boolean = false,
axis: 'both' | 'x' | 'y' | 'none' = 'both',
minConstraints: [number, number] = [10, 10],
maxConstraints: [number, number] = [Infinity, Infinity],
onResizeStop?: ?(e: SyntheticEvent, data: ResizeCallbackData) => any,
onResizeStart?: ?(e: SyntheticEvent, data: ResizeCallbackData) => any,
onResize?: ?(e: SyntheticEvent, data: ResizeCallbackData) => any,
draggableOpts?: ?Object
};