1. 使用多列布局
多列布局是一种十分简单的实现瀑布流的方法,并且兼容性也很好。
html:
html
<template>
<div class="app">
<div v-for="(item, i) in imgList" :key="item.img" class="item">
<img :src="item.img" alt="" />
<p>{{ i }}</p>
</div>
</div>
</template>
为.app 添加column-count属性,.app容器会把内容分为指定数量列,容器内的内容会平均分配在指定的列中,column-gap 可以指定每个列之间的间距。
css
.app {
column-count: 5;
column-gap: 10px;
background-color: aquamarine;
.item {
height: fit-content;
overflow: hidden;
position: relative;
img {
width: 100%;
height: 100%;
object-fit: cover;
}
p {
position: absolute;
left: 0;
top: 0;
background-color: #000;
color: #fff;
font-weight: 600;
}
}
}
效果图:
多列布局瀑布流的缺点:
-
多列布局中的元素是从上至下排列的,不是常规的从左至右,有可能会导致应该排在前面的元素在最后显示。
-
多列布局的响应式兼容不是很好。
我们可以通过媒体查询来完善兼容性:
css
@media screen and (max-width: 1920px) {
.app {
column-count: 5;
}
}
@media screen and (max-width: 1200px) {
.app {
column-count: 4;
}
}
@media screen and (max-width: 800px) {
.app {
column-count: 3;
}
}
@media screen and (max-width: 500px) {
.app {
column-count: 2;
}
}
效果图:
2. 使用flex布局(横向瀑布流)
我认为这个方法除了只能横向瀑布流布局应该也没其他缺点了。
scss:
css
.app {
display: flex;
flex-wrap: wrap;
background-color: aquamarine;
.item {
position: relative;
overflow: hidden;
height: 200px;
width: fit-content;
margin-bottom: 10px;
margin-right: 10px;
flex-grow: 1; // 平均分配剩余空间
img {
width: 100%;
height: 100%;
object-fit: cover;
}
p {
position: absolute;
left: 0;
top: 0;
background-color: #000;
color: #fff;
font-weight: 600;
}
}
}
为父元素添加display: flex; 子元素添加flex-grow:1; ,子元素横向排列并且自动填充空白区域。flex-wrap: wrap;控制自动换行。
演示:
3. 使用Grid布局
grid瀑布流结合了css和js,算是一个比较中规中矩的方案吧,实现起来也比较简单。
实现效果:
css代码:
css
.app {
background-color: aquamarine;
display: grid;
grid-template-columns: repeat(auto-fit, minmax(500px, 1fr));
grid-auto-rows: 1px;
grid-gap: 0 10px;
.item {
height: fit-content;
overflow: hidden;
position: relative;
grid-row-start: auto;
img {
width: 100%;
height: 100%;
object-fit: cover;
}
p {
position: absolute;
left: 0;
top: 0;
background-color: #000;
color: #fff;
font-weight: 600;
font-size: 2vw;
}
}
}
grid-template-columns: repeat(auto-fit, minmax(500px, 1fr)); 规定了在同一行的图片最小500px,最大1fr,自动填充剩余空间。
grid-auto-rows: 1px; 的作用是把每一个网格轨道高度划为1px,方便后面js可以更好的计算。
grid-gap: 0 10px;规定行之间间隙为0,列之间的间隙为10px,为什么需要设置行之间的间隙为0呢,因为每行为1px,每个元素会占用很多行,设置了行间距,每行之间的距离会变得很大。
js代码:
js
function () {
const items = document.querySelectorAll(".item");
items.forEach((item) => {
const height = Number.parseInt(
getComputedStyle(item).getPropertyValue("height")
);
item.style.height = height + "px";
item.style.gridRowEnd = "span " + (height + SPACE);
});
};
核心思想是通过设置 grid-row-end
属性来实现瀑布流效果。具体而言,将其设置为 "span (高度 + SPACE)",其中 SPACE 表示每个 item 的行间距,为10。这样就可以将元素的高度加上行间距后转换为行的数量(每行高度为1px)。这样一来,就设置了每个元素的真实高度。
至于为什么要设置 grid-auto-rows
和 grid-row-end
属性,是因为如果不设置的话,grid 布局会将每一行的高度统一为最高的元素的高度,这样就无法实现不同行高度的瀑布流效果。通过设置 grid-row-end
属性,可以确保每个元素真正占用的高度,后续元素可以紧贴在上一个元素的下方,以实现瀑布流的布局效果。
如果grid-auto-rows: 为1fr:
可以看见,下面的元素不会填充上面的空白,每行的高度就是最高的元素的高度。
完整代码:
js
<template>
<div class="app">
<div v-for="(item, i) in imgList" :key="item.img" class="item">
<img :src="item.img" alt="" />
<p>{{ i }}</p>
</div>
</div>
</template>
<script setup>
import { onMounted, ref } from "vue";
const imgList = ref(
randomArr([
{
img: "http://43.143.250.198:8081/static/2023/10/28/5b78e5ac5e3744b6a5bfbbd7d8d41d7b.png",
},
{
img: "http://43.143.250.198:8081/static/2023/10/31/8d3e4b89cb4b44bb91baec5bb5311db3.png",
},
{
img: "https://ts1.cn.mm.bing.net/th/id/R-C.07a7f5800d36b20bc6f5b255f389bfd4?rik=OOTL%2b5c6kufU6g&riu=http%3a%2f%2fimg.pconline.com.cn%2fimages%2fupload%2fupc%2ftx%2fphotoblog%2f1009%2f03%2fc0%2f5069852_5069852_1283447495046.jpg&ehk=N835az5AWRlUec59qpFJVJzTHeur9NApCHjnu5gGBao%3d&risl=&pid=ImgRaw&r=0",
},
{
img: "http://43.143.250.198:8081/static/2023/10/31/62ea40fbd4a24743b2f4780f80bd3ff0.png",
},
{
img: "https://ts1.cn.mm.bing.net/th/id/R-C.07a7f5800d36b20bc6f5b255f389bfd4?rik=OOTL%2b5c6kufU6g&riu=http%3a%2f%2fimg.pconline.com.cn%2fimages%2fupload%2fupc%2ftx%2fphotoblog%2f1009%2f03%2fc0%2f5069852_5069852_1283447495046.jpg&ehk=N835az5AWRlUec59qpFJVJzTHeur9NApCHjnu5gGBao%3d&risl=&pid=ImgRaw&r=0",
},
{
img: "http://43.143.250.198:8081/static/2023/10/31/bd6452b755d642e79e572ee574609564.png",
},
{
img: "http://43.143.250.198:8081/static/2023/10/28/5b78e5ac5e3744b6a5bfbbd7d8d41d7b.png",
},
{
img: "http://43.143.250.198:8081/static/2023/10/31/8d3e4b89cb4b44bb91baec5bb5311db3.png",
},
{
img: "https://ts1.cn.mm.bing.net/th/id/R-C.07a7f5800d36b20bc6f5b255f389bfd4?rik=OOTL%2b5c6kufU6g&riu=http%3a%2f%2fimg.pconline.com.cn%2fimages%2fupload%2fupc%2ftx%2fphotoblog%2f1009%2f03%2fc0%2f5069852_5069852_1283447495046.jpg&ehk=N835az5AWRlUec59qpFJVJzTHeur9NApCHjnu5gGBao%3d&risl=&pid=ImgRaw&r=0",
},
{
img: "http://43.143.250.198:8081/static/2023/10/31/62ea40fbd4a24743b2f4780f80bd3ff0.png",
},
{
img: "https://ts1.cn.mm.bing.net/th/id/R-C.07a7f5800d36b20bc6f5b255f389bfd4?rik=OOTL%2b5c6kufU6g&riu=http%3a%2f%2fimg.pconline.com.cn%2fimages%2fupload%2fupc%2ftx%2fphotoblog%2f1009%2f03%2fc0%2f5069852_5069852_1283447495046.jpg&ehk=N835az5AWRlUec59qpFJVJzTHeur9NApCHjnu5gGBao%3d&risl=&pid=ImgRaw&r=0",
},
{
img: "http://43.143.250.198:8081/static/2023/10/31/bd6452b755d642e79e572ee574609564.png",
},
{
img: "http://43.143.250.198:8081/static/2023/10/28/5b78e5ac5e3744b6a5bfbbd7d8d41d7b.png",
},
{
img: "http://43.143.250.198:8081/static/2023/10/31/8d3e4b89cb4b44bb91baec5bb5311db3.png",
},
{
img: "https://ts1.cn.mm.bing.net/th/id/R-C.07a7f5800d36b20bc6f5b255f389bfd4?rik=OOTL%2b5c6kufU6g&riu=http%3a%2f%2fimg.pconline.com.cn%2fimages%2fupload%2fupc%2ftx%2fphotoblog%2f1009%2f03%2fc0%2f5069852_5069852_1283447495046.jpg&ehk=N835az5AWRlUec59qpFJVJzTHeur9NApCHjnu5gGBao%3d&risl=&pid=ImgRaw&r=0",
},
{
img: "http://43.143.250.198:8081/static/2023/10/31/62ea40fbd4a24743b2f4780f80bd3ff0.png",
},
{
img: "https://ts1.cn.mm.bing.net/th/id/R-C.07a7f5800d36b20bc6f5b255f389bfd4?rik=OOTL%2b5c6kufU6g&riu=http%3a%2f%2fimg.pconline.com.cn%2fimages%2fupload%2fupc%2ftx%2fphotoblog%2f1009%2f03%2fc0%2f5069852_5069852_1283447495046.jpg&ehk=N835az5AWRlUec59qpFJVJzTHeur9NApCHjnu5gGBao%3d&risl=&pid=ImgRaw&r=0",
},
{
img: "http://43.143.250.198:8081/static/2023/10/31/bd6452b755d642e79e572ee574609564.png",
},
{
img: "http://43.143.250.198:8081/static/2023/10/28/5b78e5ac5e3744b6a5bfbbd7d8d41d7b.png",
},
{
img: "http://43.143.250.198:8081/static/2023/10/31/8d3e4b89cb4b44bb91baec5bb5311db3.png",
},
{
img: "https://ts1.cn.mm.bing.net/th/id/R-C.07a7f5800d36b20bc6f5b255f389bfd4?rik=OOTL%2b5c6kufU6g&riu=http%3a%2f%2fimg.pconline.com.cn%2fimages%2fupload%2fupc%2ftx%2fphotoblog%2f1009%2f03%2fc0%2f5069852_5069852_1283447495046.jpg&ehk=N835az5AWRlUec59qpFJVJzTHeur9NApCHjnu5gGBao%3d&risl=&pid=ImgRaw&r=0",
},
{
img: "http://43.143.250.198:8081/static/2023/10/31/62ea40fbd4a24743b2f4780f80bd3ff0.png",
},
{
img: "https://ts1.cn.mm.bing.net/th/id/R-C.07a7f5800d36b20bc6f5b255f389bfd4?rik=OOTL%2b5c6kufU6g&riu=http%3a%2f%2fimg.pconline.com.cn%2fimages%2fupload%2fupc%2ftx%2fphotoblog%2f1009%2f03%2fc0%2f5069852_5069852_1283447495046.jpg&ehk=N835az5AWRlUec59qpFJVJzTHeur9NApCHjnu5gGBao%3d&risl=&pid=ImgRaw&r=0",
},
{
img: "http://43.143.250.198:8081/static/2023/10/31/bd6452b755d642e79e572ee574609564.png",
},
{
img: "http://43.143.250.198:8081/static/2023/10/28/5b78e5ac5e3744b6a5bfbbd7d8d41d7b.png",
},
{
img: "http://43.143.250.198:8081/static/2023/10/31/8d3e4b89cb4b44bb91baec5bb5311db3.png",
},
{
img: "https://ts1.cn.mm.bing.net/th/id/R-C.07a7f5800d36b20bc6f5b255f389bfd4?rik=OOTL%2b5c6kufU6g&riu=http%3a%2f%2fimg.pconline.com.cn%2fimages%2fupload%2fupc%2ftx%2fphotoblog%2f1009%2f03%2fc0%2f5069852_5069852_1283447495046.jpg&ehk=N835az5AWRlUec59qpFJVJzTHeur9NApCHjnu5gGBao%3d&risl=&pid=ImgRaw&r=0",
},
{
img: "http://43.143.250.198:8081/static/2023/10/31/62ea40fbd4a24743b2f4780f80bd3ff0.png",
},
{
img: "https://ts1.cn.mm.bing.net/th/id/R-C.07a7f5800d36b20bc6f5b255f389bfd4?rik=OOTL%2b5c6kufU6g&riu=http%3a%2f%2fimg.pconline.com.cn%2fimages%2fupload%2fupc%2ftx%2fphotoblog%2f1009%2f03%2fc0%2f5069852_5069852_1283447495046.jpg&ehk=N835az5AWRlUec59qpFJVJzTHeur9NApCHjnu5gGBao%3d&risl=&pid=ImgRaw&r=0",
},
{
img: "http://43.143.250.198:8081/static/2023/10/31/bd6452b755d642e79e572ee574609564.png",
},
])
);
function randomArr(arr) {
return arr.sort(() => (Math.random() > 0.5 ? 1 : -1));
}
onMounted(() => {
const SPACE = 10;
window.onload = function () {
const items = document.querySelectorAll(".item");
items.forEach((item) => {
const height = Number.parseInt(
getComputedStyle(item).getPropertyValue("height")
);
item.style.height = height + "px";
item.style.gridRowEnd = "span " + (height + SPACE);
});
};
});
</script>
<style lang="scss" scoped>
.app {
background-color: aquamarine;
display: grid;
grid-template-columns: repeat(auto-fit, minmax(500px, 1fr));
grid-auto-rows: 1fr;
grid-gap: 0 10px;
.item {
height: fit-content;
overflow: hidden;
position: relative;
grid-row-start: auto;
img {
width: 100%;
height: 100%;
object-fit: cover;
}
p {
position: absolute;
left: 0;
top: 0;
background-color: #000;
color: #fff;
font-weight: 600;
font-size: 2vw;
}
}
}
</style>
使用Position定位实现
Position定位大量依赖js,性能比较差,但是是目前瀑布流的主要实现方案。
①. 首先根据最小宽度,计算这个容器可以放下多少列,以及计算容器多出来的宽度可以平分给每列多少:
js
const MIN_WIDTH = 200; // 每列的最低宽度
const SPACE = 10; // 行和列的间隔
/**
* 计算适合的列,以及每列的膨胀值
*/
function calc() {
// 获取容器宽度
const width = Number.parseInt(
getComputedStyle(document.querySelector(".app")).getPropertyValue("width")
);
let colNum = width / (MIN_WIDTH + SPACE);
// 计算容器多出来的宽度可以平分给每列多少
const expension = (width - colNum * (MIN_WIDTH + SPACE)) / colNum;
return {
colNum,
expension,
};
}
②:根据算法设置元素位置:
js
/**
* 设置位置
*/
function setPosition() {
// 首先调用calc函数获取基本信息:
const info = calc();
// 根据info.colNum生成数组,记录每列的最高高度。
const topList = new Array(info.colNum);
// 填充0
topList.fill(0);
// 计算膨胀后的宽度
const EXPENSION_WIDTH = MIN_WIDTH + info.expension;
// 获取所有的瀑布流item
const items = document.querySelectorAll(".item");
// 遍历
items.forEach((item) => {
// 首先获取topList中最矮的列,让item优先填充最矮的列
const minHeight = Math.min(...topList);
// 获取minHeight的位置
const index = topList.indexOf(minHeight);
// 计算元素距离左边的值
const left = (EXPENSION_WIDTH + SPACE) * index;
// 设置元素的transform
item.style.transform = `translate(${left}px,${minHeight}px)`;
// 获取元素的高度
const itemHeight = Number.parseInt(
getComputedStyle(item).getPropertyValue("height")
);
// 更新高度,注意,要加上膨胀后的高度。
topList[index] += (EXPENSION_WIDTH * itemHeight) / MIN_WIDTH + SPACE;
// 更新元素的宽度
item.style.width = EXPENSION_WIDTH + "px";
});
// 获取父元素,设置高度为最高列的高度。
const app = document.querySelector(".app");
app.style.height = Math.max(...topList) + "px";
}
算法大致是:topList中的每一个位置记录着对应列的高度,每次遍历都会让元素去最矮的那一列填充,然后更新列的高度,但是,容器中可能会多出来一部分的元素,这个时候就要把这部分多出来的空间平分给每一列,所以,每一列的实际宽度是MIN_WIDTH 加上 平分的宽度。
这里要注意,每当一张图片加载完毕之后,都要重新计算位置,不然等到所有的元素都加载完才计算就会很不美观,并且宽度改变的时候也要重新计算。
完整代码:
html
<template>
<div class="app">
<div v-for="(item, i) in imgList" :key="item.img" class="item">
<img @load="setPosition" :src="item.img" alt="" />
<p>{{ i }}</p>
</div>
</div>
</template>
<script setup>
import { ref, onMounted } from "vue";
const imgList = ref([
{
img: "http://43.143.250.198:8081/static/2023/10/28/5b78e5ac5e3744b6a5bfbbd7d8d41d7b.png",
},
{
img: "http://43.143.250.198:8081/static/2023/10/31/8d3e4b89cb4b44bb91baec5bb5311db3.png",
},
{
img: "https://ts1.cn.mm.bing.net/th/id/R-C.07a7f5800d36b20bc6f5b255f389bfd4?rik=OOTL%2b5c6kufU6g&riu=http%3a%2f%2fimg.pconline.com.cn%2fimages%2fupload%2fupc%2ftx%2fphotoblog%2f1009%2f03%2fc0%2f5069852_5069852_1283447495046.jpg&ehk=N835az5AWRlUec59qpFJVJzTHeur9NApCHjnu5gGBao%3d&risl=&pid=ImgRaw&r=0",
},
{
img: "http://43.143.250.198:8081/static/2023/10/31/62ea40fbd4a24743b2f4780f80bd3ff0.png",
},
{
img: "https://ts1.cn.mm.bing.net/th/id/R-C.07a7f5800d36b20bc6f5b255f389bfd4?rik=OOTL%2b5c6kufU6g&riu=http%3a%2f%2fimg.pconline.com.cn%2fimages%2fupload%2fupc%2ftx%2fphotoblog%2f1009%2f03%2fc0%2f5069852_5069852_1283447495046.jpg&ehk=N835az5AWRlUec59qpFJVJzTHeur9NApCHjnu5gGBao%3d&risl=&pid=ImgRaw&r=0",
},
{
img: "http://43.143.250.198:8081/static/2023/10/31/bd6452b755d642e79e572ee574609564.png",
},
{
img: "http://43.143.250.198:8081/static/2023/10/28/5b78e5ac5e3744b6a5bfbbd7d8d41d7b.png",
},
{
img: "http://43.143.250.198:8081/static/2023/10/31/8d3e4b89cb4b44bb91baec5bb5311db3.png",
},
{
img: "https://ts1.cn.mm.bing.net/th/id/R-C.07a7f5800d36b20bc6f5b255f389bfd4?rik=OOTL%2b5c6kufU6g&riu=http%3a%2f%2fimg.pconline.com.cn%2fimages%2fupload%2fupc%2ftx%2fphotoblog%2f1009%2f03%2fc0%2f5069852_5069852_1283447495046.jpg&ehk=N835az5AWRlUec59qpFJVJzTHeur9NApCHjnu5gGBao%3d&risl=&pid=ImgRaw&r=0",
},
{
img: "http://43.143.250.198:8081/static/2023/10/31/62ea40fbd4a24743b2f4780f80bd3ff0.png",
},
{
img: "https://ts1.cn.mm.bing.net/th/id/R-C.07a7f5800d36b20bc6f5b255f389bfd4?rik=OOTL%2b5c6kufU6g&riu=http%3a%2f%2fimg.pconline.com.cn%2fimages%2fupload%2fupc%2ftx%2fphotoblog%2f1009%2f03%2fc0%2f5069852_5069852_1283447495046.jpg&ehk=N835az5AWRlUec59qpFJVJzTHeur9NApCHjnu5gGBao%3d&risl=&pid=ImgRaw&r=0",
},
{
img: "http://43.143.250.198:8081/static/2023/10/31/bd6452b755d642e79e572ee574609564.png",
},
{
img: "http://43.143.250.198:8081/static/2023/10/28/5b78e5ac5e3744b6a5bfbbd7d8d41d7b.png",
},
{
img: "http://43.143.250.198:8081/static/2023/10/31/8d3e4b89cb4b44bb91baec5bb5311db3.png",
},
{
img: "https://ts1.cn.mm.bing.net/th/id/R-C.07a7f5800d36b20bc6f5b255f389bfd4?rik=OOTL%2b5c6kufU6g&riu=http%3a%2f%2fimg.pconline.com.cn%2fimages%2fupload%2fupc%2ftx%2fphotoblog%2f1009%2f03%2fc0%2f5069852_5069852_1283447495046.jpg&ehk=N835az5AWRlUec59qpFJVJzTHeur9NApCHjnu5gGBao%3d&risl=&pid=ImgRaw&r=0",
},
{
img: "http://43.143.250.198:8081/static/2023/10/31/62ea40fbd4a24743b2f4780f80bd3ff0.png",
},
{
img: "https://ts1.cn.mm.bing.net/th/id/R-C.07a7f5800d36b20bc6f5b255f389bfd4?rik=OOTL%2b5c6kufU6g&riu=http%3a%2f%2fimg.pconline.com.cn%2fimages%2fupload%2fupc%2ftx%2fphotoblog%2f1009%2f03%2fc0%2f5069852_5069852_1283447495046.jpg&ehk=N835az5AWRlUec59qpFJVJzTHeur9NApCHjnu5gGBao%3d&risl=&pid=ImgRaw&r=0",
},
{
img: "http://43.143.250.198:8081/static/2023/10/31/bd6452b755d642e79e572ee574609564.png",
},
{
img: "http://43.143.250.198:8081/static/2023/10/28/5b78e5ac5e3744b6a5bfbbd7d8d41d7b.png",
},
{
img: "http://43.143.250.198:8081/static/2023/10/31/8d3e4b89cb4b44bb91baec5bb5311db3.png",
},
{
img: "https://ts1.cn.mm.bing.net/th/id/R-C.07a7f5800d36b20bc6f5b255f389bfd4?rik=OOTL%2b5c6kufU6g&riu=http%3a%2f%2fimg.pconline.com.cn%2fimages%2fupload%2fupc%2ftx%2fphotoblog%2f1009%2f03%2fc0%2f5069852_5069852_1283447495046.jpg&ehk=N835az5AWRlUec59qpFJVJzTHeur9NApCHjnu5gGBao%3d&risl=&pid=ImgRaw&r=0",
},
{
img: "http://43.143.250.198:8081/static/2023/10/31/62ea40fbd4a24743b2f4780f80bd3ff0.png",
},
{
img: "https://ts1.cn.mm.bing.net/th/id/R-C.07a7f5800d36b20bc6f5b255f389bfd4?rik=OOTL%2b5c6kufU6g&riu=http%3a%2f%2fimg.pconline.com.cn%2fimages%2fupload%2fupc%2ftx%2fphotoblog%2f1009%2f03%2fc0%2f5069852_5069852_1283447495046.jpg&ehk=N835az5AWRlUec59qpFJVJzTHeur9NApCHjnu5gGBao%3d&risl=&pid=ImgRaw&r=0",
},
{
img: "http://43.143.250.198:8081/static/2023/10/31/bd6452b755d642e79e572ee574609564.png",
},
{
img: "http://43.143.250.198:8081/static/2023/10/28/5b78e5ac5e3744b6a5bfbbd7d8d41d7b.png",
},
{
img: "http://43.143.250.198:8081/static/2023/10/31/8d3e4b89cb4b44bb91baec5bb5311db3.png",
},
{
img: "https://ts1.cn.mm.bing.net/th/id/R-C.07a7f5800d36b20bc6f5b255f389bfd4?rik=OOTL%2b5c6kufU6g&riu=http%3a%2f%2fimg.pconline.com.cn%2fimages%2fupload%2fupc%2ftx%2fphotoblog%2f1009%2f03%2fc0%2f5069852_5069852_1283447495046.jpg&ehk=N835az5AWRlUec59qpFJVJzTHeur9NApCHjnu5gGBao%3d&risl=&pid=ImgRaw&r=0",
},
{
img: "http://43.143.250.198:8081/static/2023/10/31/62ea40fbd4a24743b2f4780f80bd3ff0.png",
},
{
img: "https://ts1.cn.mm.bing.net/th/id/R-C.07a7f5800d36b20bc6f5b255f389bfd4?rik=OOTL%2b5c6kufU6g&riu=http%3a%2f%2fimg.pconline.com.cn%2fimages%2fupload%2fupc%2ftx%2fphotoblog%2f1009%2f03%2fc0%2f5069852_5069852_1283447495046.jpg&ehk=N835az5AWRlUec59qpFJVJzTHeur9NApCHjnu5gGBao%3d&risl=&pid=ImgRaw&r=0",
},
{
img: "http://43.143.250.198:8081/static/2023/10/31/bd6452b755d642e79e572ee574609564.png",
},
]);
const MIN_WIDTH = 500; // 每列的最低宽度
const SPACE = 10; // 行和列的间隔
/**
* 计算适合的列,以及每列的膨胀值
*/
function calc() {
// 获取容器宽度
const width = Number.parseInt(
getComputedStyle(document.querySelector(".app")).getPropertyValue("width")
);
let colNum = Math.floor(width / (MIN_WIDTH + SPACE));
// 计算容器多出来的宽度可以平分给每列多少
const expension = Math.floor((width - colNum * (MIN_WIDTH + SPACE)) / colNum);
return {
colNum,
expension,
};
}
/**
* 设置位置
*/
function setPosition() {
// 首先调用calc函数获取基本信息:
const info = calc();
// 根据info.colNum生成数组,记录每列的最高高度。
const topList = new Array(info.colNum);
// 填充0
topList.fill(0);
// 计算膨胀后的宽度
const EXPENSION_WIDTH = MIN_WIDTH + info.expension;
// 获取所有的瀑布流item
const items = document.querySelectorAll(".item");
// 遍历
items.forEach((item) => {
// 首先获取topList中最矮的列,让item优先填充最矮的列
const minHeight = Math.min(...topList);
// 获取minHeight的位置
const index = topList.indexOf(minHeight);
// 计算元素距离左边的值
const left = (EXPENSION_WIDTH + SPACE) * index;
// 设置元素的transform
item.style.transform = `translate(${left}px,${minHeight}px)`;
// 获取元素的高度
const itemHeight = Number.parseInt(
getComputedStyle(item).getPropertyValue("height")
);
// 更新高度,注意,要加上膨胀后的高度。
topList[index] += (EXPENSION_WIDTH * itemHeight) / MIN_WIDTH + SPACE;
// 更新元素的宽度
item.style.width = EXPENSION_WIDTH + "px";
});
// 获取父元素,设置高度为最高列的高度。
const app = document.querySelector(".app");
app.style.height = Math.max(...topList) + "px";
}
function throttle(cb, time = 200) {
let timer = null;
return function () {
if (timer) return;
timer = setTimeout(() => {
cb();
timer = null;
}, time);
};
}
onMounted(() => {
window.addEventListener("resize", throttle(setPosition));
});
</script>
<style lang="scss" scoped>
.app {
column-count: 5;
column-gap: 10px;
background-color: aquamarine;
position: relative;
.item {
height: fit-content;
overflow: hidden;
position: absolute;
transition: all 0.3s;
width: 500px;
img {
width: 100%;
height: 100%;
object-fit: cover;
}
p {
position: absolute;
left: 0;
top: 0;
background-color: #000;
color: #fff;
font-weight: 600;
font-size: 2vw;
}
}
}
</style>