【前端】关于前端瀑布流的几种实现方案

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;
    }
  }
}

效果图:

多列布局瀑布流的缺点:

  1. 多列布局中的元素是从上至下排列的,不是常规的从左至右,有可能会导致应该排在前面的元素在最后显示。

  2. 多列布局的响应式兼容不是很好。

我们可以通过媒体查询来完善兼容性:

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-rowsgrid-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>
相关推荐
阿伟来咯~9 分钟前
记录学习react的一些内容
javascript·学习·react.js
吕彬-前端15 分钟前
使用vite+react+ts+Ant Design开发后台管理项目(五)
前端·javascript·react.js
学前端的小朱17 分钟前
Redux的简介及其在React中的应用
前端·javascript·react.js·redux·store
guai_guai_guai26 分钟前
uniapp
前端·javascript·vue.js·uni-app
也无晴也无风雨27 分钟前
在JS中, 0 == [0] 吗
开发语言·javascript
bysking1 小时前
【前端-组件】定义行分组的表格表单实现-bysking
前端·react.js
王哲晓2 小时前
第三十章 章节练习商品列表组件封装
前端·javascript·vue.js
fg_4112 小时前
无网络安装ionic和运行
前端·npm
理想不理想v2 小时前
‌Vue 3相比Vue 2的主要改进‌?
前端·javascript·vue.js·面试
酷酷的阿云2 小时前
不用ECharts!从0到1徒手撸一个Vue3柱状图
前端·javascript·vue.js