记录下瀑布流布局实现方案
生成指定大小的随机图片: https://picsum.photos/width/height
初始页面如下,共有 10 张宽度固定,高度随机的图片
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>瀑布流</title>
</head>
<body>
<div class="container"></div>
</body>
<script>
// 生成随机图片
const WIDTH = 300
const HEIGHT_ARR = [200, 250, 300, 350, 400, 450, 500, 600]
const getRandomUrl = () => {
const height = HEIGHT_ARR[Math.floor(Math.random() * 8)]
return `https://picsum.photos/${WIDTH}/${height}`
}
function generateImages(count = 10) {
const container = document.querySelector('.container')
for (let i = 0; i < count; i++) {
const img = document.createElement('img')
img.src = getRandomUrl()
container.appendChild(img)
}
}
generateImages()
</script>
</html>
方案一:纯 css 布局
将 container 设置为 grid 布局,固定列宽
实现核心是 grid-template-rows: masonry;
.container {
display: grid;
grid-template-columns: repeat(4, 1fr);
grid-template-rows: masonry;
grid-gap: 10px;
}
caniuse 网站显示,至今只有最新版的 Safari 浏览器支持这一特性。。。
方案二:js 计算布局
整体布局信息
根据容器宽度和图片宽度,计算共有多少列,剩余空间平均分配,作为每列的间隙
function cal() {
const containerWidth = container.clientWidth
const columns = Math.floor(containerWidth / IMG_WIDTH) // 总列数
const spaceNumber = columns + 1 // 间隙个数
// 剩余空间平均分配,作为间隙
const leftSpace = containerWidth - columns * IMG_WIDTH
const space = leftSpace / spaceNumber
return {
space,
columns
}
}
元素的定位
设置定位:容器 relative,图片 absolute
思路:维护一个数组,数组每项记录每列的当前高度,那么下一张照片就放在最小高度的那一列
用列数作为 length,初始化数组 nextTops,默认坐标为 0
循环每个 img 元素
- 找到 nextTops 中的最小高度,作为 img 的 top
- 更新该列的纵坐标(记得算上间隙)
- 根据更新列 nextTops 的 index 位置(用最小高度去找),计算图片的 left(加上间隙)
- 最后设置容器的高度为 nextTops 的最大值,即最大列高
// 以计算出来的列数为 length,初始化纵坐标数组,默认坐标为 0
const info = cal()
const nextTops = new Array(info.columns)
nextTops.fill(0)
// 循环每个 img 元素
for (let i = 0; i < container.children.length; i++) {
const img = container.children[i]
// 找到列中的最小高度,作为图片的 top
const minTop = Math.min.apply(null, nextTops)
img.style.top = minTop + 'px'
// 更新该列的纵坐标(记得算上间隙)
const index = nextTops.indexOf(minTop)
nextTops[index] += img.height + info.space
// 根据列的位置,计算图片的 left
const left = (index + 1) * info.space + index * IMG_WIDTH
img.style.left = left + 'px'
}
// 设置容器高度为最大纵坐标的列高
const max = Math.max.apply(null, nextTops)
container.style.height = max + 'px'
效果如下