实现思路是:
- 收集列表元素数组
- 每列定宽不定高,列表元素全部相对于容器元素绝对定位
- 根据容器可视宽度计算每行的元素盛放个数
- 根据盛放个数先处理第一行,依次排列在第一行
- 处理后续元素,维护一个每列当前高度的数组,对于后续行的元素,依次往当前最低的那一列上放置直到最后一个元素
- 最后由于列表元素全部绝对定位导致容器元素无法高度自适应,在放置完最后一个列表元素而后将容器元素的高度设定为数组的最大值即可
代码如下:
<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
<style>
body {
margin: 0;
padding: 0;
}
</style>
</head>
<body>
<div id="app">
</div>
<script>
//面向瀑布流对象编程
class WaterFull {
constructor (boxes, app) {
this.clientWidth = document.documentElement.clientWidth
this.boxes = boxes
this.app = app
this.boxWidth = this.boxes[0].offsetWidth
this.maxColumn = parseInt(this.clientWidth / this.boxWidth)
this.heightArr = []
}
init () {
// 设置容器宽度
this.app.style.width = (this.maxColumn * this.boxes[0].offsetWidth) + 'px'
// 处理第一行
this.processFirstLine()
// 处理其他行
this.processOtherLine()
}
processFirstLine () {
for (let i = 0; i < this.maxColumn; i++) {
let box = this.boxes[i]
box.style.position = 'absolute'
box.style.left = (i * this.boxWidth) + 'px'
box.style.top = '0px'
this.heightArr.push(box.offsetHeight)
}
}
processOtherLine () {
for (let i = this.maxColumn; i < this.boxes.length; i++) {
let box = this.boxes[i]
let minHeight = Math.min(...this.heightArr)
let minIndex = this.heightArr.indexOf(minHeight)
box.style.position = 'absolute'
box.style.left = (minIndex * this.boxWidth) + 'px'
box.style.top = minHeight + 'px'
this.heightArr[minIndex] += box.offsetHeight
if (i + 1 === this.boxes.length) {
this.app.style.height = Math.max(...this.heightArr) + 'px'
}
}
}
}
function initRender () {
const app = document.querySelector('#app')
const boxes = []
for (let i = 0; i < 100; i++) {
let box = document.createElement('div')
box.style.boxSizing = 'border-box'
box.style.borderRadius = '10px'
box.style.height = getRandomHeight() + 'px'
box.style.width = '300px'
box.style.backgroundColor = getRandomColor()
box.classList.add('box')
app.appendChild(box)
boxes.push(box)
}
let wf = new WaterFull(boxes, app)
wf.init()
}
initRender()
function getRandomHeight() {
return parseInt(Math.random() * 300 + 100)
}
function getRandomColor() {
function random () {
return parseInt(Math.random() * 255 + 1)
}
return `rgba(${random()}, ${random()}, ${random()}, 1)`
}
</script>
</body>
</html>
效果如下: