最近在进行开发时,遇到需要动态拉升左侧区域,从而改变左右两栏尺寸的布局需求。这是一个很常见的场景,通过 DOM 操作来实现是第一个想到的方法。仔细查找资料,在大神张鑫旭的博客上发现还有一个性能更好,利用纯 CSS 就能实现分栏宽度拉伸调整的效果。下面的代码和部分内容引用自该博客:
首先要实现的效果,拖拽分隔线,动态改变左右两栏尺寸。

一、HTML 代码:
1 2 3 4 5 6 7 8 9 10 11
| <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 代码
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59
| .column { overflow: hidden; } .column-left { height: 400px; 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; }
@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%; } }
|
~ 表示兄弟组合器,类似于相邻的兄弟组合器。区别在于,第二个选择器不必紧跟第一个选择器,这意味着它将选择第一个选择器后面的所有元素。
1
| div ~ p { background-color: blue;}
|
1 2 3 4 5 6
| <div id = "container"> <p>First </p> <div><p>Child Paragraph</p> </div> <p> Second </p> <p> Third </p> </div>
|
三、原理
利用浏览器非 overflow: auto 的属性,设置元素 resize 可以拉伸的特性,从而实现纯 CSS 的分栏宽度控制。
webkit 浏览器下滚动条可以自定义,其中 resize 区域大小就是 scrollbar 的大小,于是,我们就可以将整个拉伸区域变成和容器一样高。
四、属性 resize 参数
resize 属性主要是用来改变元素尺寸大小的,其主要目的是增强用户体验。
1
| resize: none | both | horizontal | vertical | inherit
|
在 CSS3 中 resize 属性指定的值分为以下几种。
- none: 用户不能拖动元素修改尺寸大小。
- both: 用户可以拖动元素,同时修改元素的高度和宽度。
- horizontal: 用户可以拖动元素,仅可以改变元素的宽度,但不能修改元素的高度;
- vertical: 用户可以拖动元素,仅可以改变元素的高度,但不能修改元素的宽度;
- inherit: 继承父元素的 resize 属性值。
五、利用组件 react-resizable

1、安装 react-resizable
1
| npm install --save react-resizable
|
2、使用 react-resizable, demo 如下
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63
| import React from 'react'; const Resizable = require('react-resizable').Resizable; const ResizableBox = require('react-resizable').ResizableBox;
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> ); } }
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20
| .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>
的参数
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
| { children: React.Element<any>, width: number, height: number, 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 };
|