大家好,欢迎来到IT知识分享网。
前端功能问题系列文章,点击上方合集↑
序言
大家好,我是大澈!
本文约2500+
字,整篇阅读大约需要4分钟。
本文主要内容分三部分,如果您只需要解决问题,请阅读第一、二部分即可。如果您有更多时间,进一步学习问题相关知识点,请阅读至第三部分。
感谢关注微信公众号:“程序员大澈”,然后加入问答群
,从此让解决问题的你不再孤单!
1. 需求分析
在电商前台项目中,使用瀑布流的布局形式,展示商品图片列表。
让用户在浏览商品列表时,总有商品图片看不全,使用户可以无限滚动地查看列表下面的其它商品内容。
进而提高用户浏览量,增加商品曝光度,最终提升用户购买几率。
2. 实现步骤
2.1 什么是瀑布流布局呢
瀑布流布局,是页面上是一种参差不齐的多栏布局。
随着页面滚动条向下滚动,这种布局会不断加载新的数据内容,并附加至当前高度最低列的尾部。
它的特点
是:布局宽度一致,高度不一致,上下错落排列。一般用于图片内容的展示。
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