CSS问题:如何实现瀑布流布局?

CSS问题:如何实现瀑布流布局?在电商前台项目中 使用瀑布流的布局形式 展示商品图片列表

大家好,欢迎来到IT知识分享网。

前端功能问题系列文章,点击上方合集↑

序言

大家好,我是大澈!

本文约2500+字,整篇阅读大约需要4分钟。

本文主要内容分三部分,如果您只需要解决问题,请阅读第一、二部分即可。如果您有更多时间,进一步学习问题相关知识点,请阅读至第三部分。

感谢关注微信公众号:“程序员大澈”,然后加入问答群,从此让解决问题的你不再孤单!

1. 需求分析

在电商前台项目中,使用瀑布流的布局形式,展示商品图片列表。

让用户在浏览商品列表时,总有商品图片看不全,使用户可以无限滚动地查看列表下面的其它商品内容。

进而提高用户浏览量,增加商品曝光度,最终提升用户购买几率。

CSS问题:如何实现瀑布流布局?

2. 实现步骤

2.1 什么是瀑布流布局呢

瀑布流布局,是页面上是一种参差不齐的多栏布局。

随着页面滚动条向下滚动,这种布局会不断加载新的数据内容,并附加至当前高度最低列的尾部。

它的特点是:布局宽度一致,高度不一致,上下错落排列。一般用于图片内容的展示。

CSS问题:如何实现瀑布流布局?

2.2 代码实现

模版代码:

一行要展示几条数据,就定义几个column元素。这里以三列为例。

<template>   <div class="page-main">     <div class="card">       <div class="coloum1">         <div           class="card-item"           v-for="(item, index) in cardList1"           :key="index"           :style="[             { background: item.color },             { height: item.height },             { lineHeight: item.height },           ]"           :class="{ visibles: isVisibility }"         >           <p class="text">{ 
  { item.num }}</p>         </div>       </div>       <div class="coloum2">         <div           class="card-item"           v-for="(item, index) in cardList2"           :key="index"           :style="[             { background: item.color },             { height: item.height },             { lineHeight: item.height },           ]"           :class="{ visibles: isVisibility }"         >           <p class="text">{ 
  { item.num }}</p>         </div>       </div>       <div class="coloum3">         <div           class="card-item"           v-for="(item, index) in cardList3"           :key="index"           :style="[             { background: item.color },             { height: item.height },             { lineHeight: item.height },           ]"           :class="{ visibles: isVisibility }"         >           <p class="text">{ 
  { item.num }}</p>         </div>       </div>     </div>   </div> </template> 

逻辑代码:

第一次渲染时,先把已有数据按顺序正常展示。

然后利用nextTick钩子,在第二次渲染时,先获取所有元素,再循环遍历所有元素,再从第二行第一个元素开始,计算每一列高度和的最小值,把新数据放到最小高度列的数组数据中。以此类推,判断完所有已获取元素。

这里在两次渲染之间,可能会出现页面闪烁现象,所以做了元素显示隐藏的样式处理。

再就是,有多少列,则定义多少新的空数组。这里以三列为例。

<script setup> import { ref, onMounted, reactive, nextTick } from "vue"; // 展示数据 const cardList = reactive([   {     num: "1号 100px",     color: "#3498db",     height: "100px",   },   {     num: "2号 200px",     color: "#2ecc71",     height: "200px",   },   {     num: "3号 60px",     color: "#27ae60",     height: "60px",   },   {     num: "4号 80px",     color: "#e67e22",     height: "80px",   },   {     num: "5号 60px",     color: "#e74c3c",     height: "60px",   },   {     num: "6号 200px",     color: "#7f8c8d",     height: "200px",   }, ]); // 由于渲染时候对数据的两次赋值,则会出现一次闪烁,需要做显示隐藏处理 const isVisibility = ref(true); onMounted(() => {   // 第一次渲染赋值   equallyCard();   // 第二次渲染赋值   nextTick(() => {     caLFlex();   })   .then(() => {     // 闪烁显示隐藏处理     isVisibility.value = true;   }); }); // 各列的展示数据 const cardList1 = ref([]); const cardList2 = ref([]); const cardList3 = ref([]); // 第一次渲染赋值 function equallyCard() {   // 平分3列的数据,确保页面上遍历卡片dom的真实顺序与平分的一致   let num = parseInt(cardList.length / 3);   cardList.forEach((item, index) => {     if (index < num) {       cardList1.value.push(item);       return;     }     if (index < 2 * num) {       cardList2.value.push(item);       return;     }     cardList3.value.push(item);   }); } // 第二次渲染赋值 function caLFlex() {   let arr1 = []; // 第一列的新值   let arr2 = []; // 第二列的新值   let arr3 = []; // 第三列的新值   let heightArry_1 = []; // 第一列的卡片高度   let heightArry_2 = []; // 第二列的卡片高度   let heightArry_3 = []; // 第三列的卡片高度   Array.from(document.querySelectorAll(".card-item")).forEach((item, index) => {     // 第一行中的元素无需判断,直接加到新数组中     if (index === 0) {       heightArry_1.push(item.offsetHeight);       arr1.push(cardList[index]);       return;     }     if (index === 1) {       heightArry_2.push(item.offsetHeight);       arr2.push(cardList[index]);       return;     }     if (index === 2) {       heightArry_3.push(item.offsetHeight);       arr3.push(cardList[index]);       return;     }     // 计算每一列高度     const heightTotal_1 = heightArry_1.length       ? Array.from(heightArry_1).reduce((accumulator, currentValue) => accumulator + currentValue)       : 0; // 第一列的总高度     const heightTotal_2 = heightArry_2.length       ? Array.from(heightArry_2).reduce((accumulator, currentValue) => accumulator + currentValue)       : 0; // 第二列的总高度     const heightTotal_3 = heightArry_3.length       ? Array.from(heightArry_3).reduce((accumulator, currentValue) => accumulator + currentValue)       : 0; // 第三列的总高度     // 找到高度最小值,并在最小高度新数组中添加新数据     let minNumber = Math.min(heightTotal_1, heightTotal_2, heightTotal_3);     switch (minNumber) {       case heightTotal_1:         heightArry_1.push(item.offsetHeight);         arr1.push(cardList[index]);         break;       case heightTotal_2:         heightArry_2.push(item.offsetHeight);         arr2.push(cardList[index]);         break;       case heightTotal_3:         heightArry_3.push(item.offsetHeight);         arr3.push(cardList[index]);         break;     }   });   // 重新将数据赋值给各列数组   cardList1.value = arr1;   cardList2.value = arr2;   cardList3.value = arr3; } </script> 

样式代码:

使用了flex布局来做行的排版。这里根据个人项目实际需求自定义即可。

<style lang="scss" scoped> .page-main {   background: #ffffff;   height: 100vh;   overflow: hidden;   padding: 0 30px;   .card {     display: flex;     flex-direction: row;     justify-content: space-around;     .card-item {       visibility: hidden;       margin-bottom: 20px;       text-align: center;       width: 216px;       border-radius: 16px;     }     .visibles {       visibility: visible;     }   } } </style>

3. 问题详解

3.1 关于NextTick的个人拙见

作用:等待DOM更新后,再执行内部传入的回调函数

使用场景:  

  • created中想要获取DOM
  • 响应式数据变化后获取DOM更新后的状态,如 获取列表更新后的高度

原理: 把nextTick回调方法放在renderWatcher回调之后执行,这样就能拿到更新后的DOM

3.2 瀑布流其它实现方式

关于瀑布流的实现方式,网上真的是五花八门,各种方法都有。

但因为精力有限,其它方式我也没有再去尝试,只挑选了这么一种比较常用的实现方式,也就是flex布局+js动态计算列高度的方式。我觉的这种方式就足够了,尝试用着还不错。

当然,本次实现的代码,不会是大澈个人空想而来,一定是站在了某位大佬的肩膀之上,又加上了一些个人的理解和拙见,才分享给了朋友们。

最后,也放上参考大佬的文章地址,大佬各种实现方式讲的挺全的,供大家参考:http://d5rhe.jbdi.cn/7b

结语

建立这个平台的初衷:

  • 打造一个仅包含前端问题的问答平台,让大家高效搜索处理同样问题。
  • 通过不断积累问题,一起练习逻辑思维,并顺便学习相关的知识点。
  • 遇到难题,遇到有共鸣的问题,一起讨论,一起沉淀,一起成长。

感谢关注微信公众号:“程序员大澈”,然后加入问答群,从此让解决问题的你不再孤单!

免责声明:本站所有文章内容,图片,视频等均是来源于用户投稿和互联网及文摘转载整编而成,不代表本站观点,不承担相关法律责任。其著作权各归其原作者或其出版社所有。如发现本站有涉嫌抄袭侵权/违法违规的内容,侵犯到您的权益,请在线联系站长,一经查实,本站将立刻删除。 本文来自网络,若有侵权,请联系删除,如若转载,请注明出处:https://haidsoft.com/122165.html

(0)
上一篇 2025-10-18 19:26
下一篇 2025-10-18 19:45

相关推荐

发表回复

您的邮箱地址不会被公开。 必填项已用 * 标注

关注微信