等宽等高,等宽不等高,等高不等宽,既不等宽又不等高。
等宽等高
利用
inline-block
或者
float
的方式即可实现
等宽不等高
使用插件实现
直接调用 Masonry 库即可实现。
使用 JS 或 CSS 实现
js 的实现方法
1
首先需要确定网页应用的宽度和每个需要放置元素的宽度,通过计算可以得到布局的列数 C 。布局元素是从主容器的左上角开始依次向下放置,因此将左上角为坐标原点(0, 0),布局元素利用相对于主容器绝对定位的方式来确定位置。维护一个长度为 C 的数组 A,分别记录该列在垂直方向上的坐标,每放置一个元素时,遍历数组 A 得到垂直坐标最小数组元素的索引 i ,计算出坐标值并分别设置布局元素的 top 和 left 值,最后更新数组 A,将索引为 i 的数组元素的值加上布局元素的高度值。
css 的实现方法
1
对于不需要考虑 css3 兼容性的页面应用,可以直接利用 flexbox 布局或者 multi-columns 布局来实现。flexbox 布局的思路是将设置主容器属性 display: "flex",排列方向设置为 flex-direction: "row",每一列用一个容器包裹,并设置该容器属性 display: "flex",排列方向设置为 flex-direction: "column",最后设置列容器的宽度值即可得到想要的布局效果。multi-columns 布局是通过设置主容器的属性 column-count 的数目来分隔出对应的列数,然后每个布局元素设置属性 break-inside: "avoid" 来避免元素跨列,只需要简单的连个属性设置就可以实现基本的布局效果,另外还可以设置主容器的 column-gap 来调整每列之间的间距。
等高不等宽
等高不等宽严格意义来说是:每一行的每一张图片等高,并不是每行都等高。
1
js 实现的思路为:设定一个基准行高 H,每个图片元素的宽高比 R 乘以 H 然后累加,每次累加后的值 S 与主容器的宽度 W 做比较,若 S > W,则最后一张图片被放置到下一行, 之前已经累加的图片元素作为一个整体,进行等比拉伸,使其宽度等于主容器宽度,这样左右两边的元素都是对齐的,不会出现空白。若要在图片横向之间加入 margin 以区分不同的图片,则宽度 W 的值为主容器宽度减去所有横向 margin 值叠加的总和。
既不等宽也不等高
在网页上应用得不多,但是在 CSS-sprite 雪碧图上经常遇到,图片的排列尽可能的利用空间,不造成大图无谓的空白占用带宽。
有一个网格布局插件 react-grid-layout 可以允许用户随意拖拽内容,拖拽后就形成了这种既不等宽也不等高的布局,就这需要用到一种叫做 集装箱算法 ,处理很多小盒子放到一个大盒子里并竟可能少留空隙。
实现思路
这篇文档 Packing Lightmaps 提出一种区域划分的方法,随着矩形的不断的放入,区域也不断的拆分,过程如下图所示:
在主容器 R 里放入矩形 A 后,主容器区域被拆分为 A 右侧区域(Aright)和下侧区域(Abottom),放置矩形 B 时, 由于 B 的宽度大于 Aright 宽度, Abottom 区域被占用,同时矩形 B 将原 Abottom 拆分为 Bright 和 Bbottom,在放入矩形 C 时会先与 Aright 区域比较宽高,若空间不够,则依次比较 Bright、Bbottom,直到找到合适的空间,放置后也是按照规则拆分出右侧和下侧区域。
由上图可以看出,这是一个类似于一个构建二叉树的问题,每插入一个子叶的过程,都从根结点开始遍历,先从左子叶开始比较有没有合适的区域位置,若没有,再到右子叶开始比较,最终得到一个放置点。有了这个思路,就可以用 js 来实现它了:
实现代码
1
class BST {
constructor(rects, width, height) {
this.root = {
w: width,
h: height,
x: 0,
y: 0,
this.rects = rects;
this.width = width;
this.height = height;
this.packedRects = [];
traversel(container, rect) {
if (container.used) {
return (
this.traversel(container.right, rect) ||
this.traversel(container.bottom, rect)
} else if (
rect.w <= container.w &&
(container.h === undefined ? true : rect.h <= container.h)
return container;
} else {
return null;
splitContainer(container, rect) {
Object.assign(container, {
used: true,
right: {
w: container.w - rect.w,
h: rect.h,
x: container.x + rect.w,
y: container.y,
bottom: {
w: container.w,
h: container.h === undefined ? undefined : container.h - rect.h,
x: rect.x,
y: container.y + rect.h,
packingRects() {
this.rects.forEach((rect, index) => {
const container = this.traversel(this.root, rect);
if (container) {
rect.x = container.x;
rect.y = container.y;
this.packedRects.push(rect);
this.splitContainer(container, rect);
return this.packedRects;
* @params rects Array eg: [{w: 2, h: 1}, {w: 1, h: 2}]
* @params width Number eg: 3
* @params height Number eg: 100
* output Array eg: [{w:2, h:1, x: 0, y: 0}, {w: 1, h: 2, x: 2, y: 0}]
function BinPacking2D(rects, width, height) {