每列的宽度相等而高度不等,且第二行的第一个容器需要放在第一行高度最小的容器下面,依次类推放置。
代码仅实现了瀑布流的布局方式和 resize 监听,如果大家有需要,可以自己拓展下:实现监听滚动事件,页面滚动加载图片的功能。
代码中写了详细注释,可以直接使用。
<!DOCTYPE html>
<html lang="en">
<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>瀑布流布局JS实现</title>
</head>
<body>
<style>
* {
margin: 0;
padding: 0;
}
body {
width: 100vw;
height: 100vh;
}
.container {
width: 100vw;
height: 100vh;
overflow: scroll;
position: relative;
background-color: lightgray;
}
.item {
width: 200px;
height: auto;
box-sizing: border-box;
background: #fff;
border: 1px solid #000;
border-radius: 15px;
text-align: center;
font-size: 40px;
font-weight: bold;
position: absolute;
top: 0;
left: 0;
}
.item1,
.item8 {
height: 300px;
}
.item2,
.item6 {
height: 150px;
}
.item3,
.item9 {
height: 120px;
}
.item4,
.item10 {
height: 270px;
}
.item5,
.item7 {
height: 350px;
}
</style>
<div class="container">
<div class="item item1">1</div>
<div class="item item2">2</div>
<div class="item item3">3</div>
<div class="item item4">4</div>
<div class="item item5">5</div>
<div class="item item6">6</div>
<div class="item item7">7</div>
<div class="item item8">8</div>
<div class="item item9">9</div>
<div class="item item10">10</div>
</div>
<script>
function waterFullLayout() {
console.log("start");
// 获取页面宽度
let pageWidth =
document.getElementsByClassName("container")[0].clientWidth;
// 获取图片固定的宽度
let itemWidth = document.getElementsByClassName("item")[0].offsetWidth;
// 设置图片之间间距
let gap = 10;
// 获取一行最多可以展示几张图片
let nums = Math.floor(pageWidth / (itemWidth + gap));
// 瀑布流实现原则:
// 所有图片元素绝对定位,从第二行开始,依次从第一行图片元素高度最小的下方填充,这里注意,不是从左至右填充,即优先填补空位,填补一个后,再填补下一个较大的空位
// 定义第一行图片的所有高度的数组,之后每张图片下方填充图片后,会更新数组对应位置下的最小高度
let heightList = [];
let itemArr = document.getElementsByClassName("item");
let imgLen = itemArr.length;
for (let i = 0; i < imgLen; i++) {
// 如果 当前图片元素索引小于一行最多可以展示的图片个数,说明是第一行,top 已在css中默认设置为0,需要统一设置 left 值,top值如果加gap,则还需设置gap值
if (i < nums) {
// 将第一行的图片元素的高度放入 heightList 数组
heightList.push(itemArr[i].offsetHeight + gap);
itemArr[i].style.top = gap + "px";
itemArr[i].style.left = (itemWidth + gap) * i + "px";
} else {
// 否则从第二行开始,要根据第一行图片元素的高度,设置top和left
// 先假设 heightList[0] 为最小高度
let minItem = {
minHeight: heightList[0],
minIndex: 0,
};
for (let j = 0; j < heightList.length; j++) {
if (heightList[j] < minItem["minHeight"]) {
minItem["minHeight"] = heightList[j];
minItem["minIndex"] = j;
}
}
itemArr[i].style.top = minItem["minHeight"] + gap + "px";
itemArr[i].style.left =
(itemWidth + gap) * minItem["minIndex"] + "px";
// 关键步骤,更新 heightList 中对应位置下整列图片的高度,以便在下次循环时根据heightList 重新寻找最小高度
heightList[minItem["minIndex"]] =
parseFloat(itemArr[i].style.top) +
parseFloat(itemArr[i].offsetHeight);
}
}
}
// 防抖
function debounce(fn, delay) {
let timer = null;
return function () {
if (timer) {
clearTimeout(timer);
}
timer = setTimeout(() => {
fn.apply(this, arguments);
}, delay);
};
}
window.onload = function () {
// 为了保证页面宽度可以正常获取,onload 之后再执行
waterFullLayout();
};
// 页面宽度变化要重新布局
window.onresize = debounce(waterFullLayout, 100);
</script>
</body>
</html>
实现后效果如下:
参考链接: