瀑布流(Waterfall Flow)是一种常见的网页布局方式,它以类似瀑布的形式将内容块依次排列,通常用于展示图片或其他具有不同高度的元素。
瀑布流布局的特点是每个内容块的宽度固定,高度不确定,并且会根据内容块的实际高度进行自动排列。这种排列方式可以让页面呈现出动态和自适应的效果,适用于展示大量不同尺寸的元素。
实现瀑布流布局通常需要通过 CSS 和 JavaScript 来实现。以下是基本的步骤:
- HTML 结构:使用
<div>或其他适当的标签包装每个内容块,并为它们添加类名或其他标识符。 - CSS 样式:设置内容块的宽度,并使其浮动(
float)或使用网格布局(grid)等方式进行排列。同时也可以设置内容块的边距、间距等样式。 - JavaScript:计算每个内容块的位置,并根据每个块的高度动态调整它们的位置。一种常见的做法是使用 JavaScript 库(如 Masonry.js)或自定义脚本来实现布局算法。
瀑布流布局的优点是在有限的空间内能够展示更多的内容,并且适应不同屏幕尺寸。它常被应用于图片展示、新闻资讯、社交网络等需要展示多个不同尺寸元素的场景。
需要注意的是,由于瀑布流布局的特性,当网页中的内容块高度不均匀且动态改变时,可能会导致布局重新计算和重新排列,这可能会影响性能。因此,在实现瀑布流布局时需要优化相关的计算和排列算法,以提高性能和用户体验。
纯代码
<template>
<div class="page-main" @touchmove="move">
<div class="card">
<div class="coloum1 left" >
<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 right">
<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>
</div>
</template>
<script setup>
import {ref,onMounted, reactive,nextTick} from 'vue'
const cardList = reactive([ // 测试数据
{
num: '0',
color: '#FCCF0A',
height: '100px',
},
{
num: '1',
color: '#FCCF0A',
height: '120px',
},
{
num: '2',
color: '#FCCF0A',
height: '130px',
},
{
num: '3',
color: '#FCCF0A',
height: '110px',
},
{
num: '4',
color: '#FCCF0A',
height: '101px',
},
{
num: '5',
color: '#FCCF0A',
height: '180px',
},
{
num: '6',
color: '#FCCF0A',
height: '130px',
},
{
num: '7',
color: '#FCCF0A',
height: '120px',
},
{
num: '8',
color: '#FCCF0A',
height: '150px',
},
{
num: '9',
color: '#FCCF0A',
height: '150px',
},
{
num: '10',
color: '#FCCF0A',
height: '200px',
},
])
const cardLis = reactive([ // 测试数据
{
num: '11',
color: '#FCCF0A',
height: '100px',
},
{
num: '12',
color: '#FCCF0A',
height: '120px',
},
{
num: '13',
color: '#FCCF0A',
height: '130px',
},
{
num: '14',
color: '#FCCF0A',
height: '110px',
},
{
num: '15',
color: '#FCCF0A',
height: '101px',
},
{
num: '16',
color: '#FCCF0A',
height: '180px',
},
{
num: '17',
color: '#FCCF0A',
height: '130px',
},
{
num: '18',
color: '#FCCF0A',
height: '120px',
},
{
num: '19',
color: '#FCCF0A',
height: '150px',
},
{
num: '20',
color: '#FCCF0A',
height: '150px',
},
{
num: '21',
color: '#FCCF0A',
height: '200px',
},
])
const isVisibility = ref(true)
// 由于渲染时候对数据的两次赋值,则会出现一次闪现,需要防抖
onMounted(()=>{
equallyCard(cardList)
nextTick(()=>{
caLFlex(cardList)
}).then(()=>{
isVisibility.value = true
})
})
const cardList1 = ref([]) // 各列的数据
const cardList2 = ref([])
function equallyCard(val){
// 平分数据,确保页面上遍历卡片dom的真实顺序与平分的一致 document.querySelectorAll('.card-item'))
let num = parseInt(val.length/2)
val.forEach((item,index)=>{
//加入数据有12条前6条归数组1后6条归数组2
if(index<num){
cardList1.value.push(item)
return
}
if(index<2*num){
cardList2.value.push(item)
return
}
})
}
let arr1 = ref([])
let arr2 = ref([])
function caLFlex(val){
let heightArry_1 = [] // 第一列的卡片高度
let heightArry_2 = [] // 第二列的卡片高度
Array.from(document.querySelectorAll('.card-item')).forEach((item,index) =>{
if(index === 0){ // 第一行中的元素无需判断,直接加到新的数组中
heightArry_1.push(item.offsetHeight)
arr1.value.push(val[index])
return
}
if(index === 1){
heightArry_2.push(item.offsetHeight)
arr2.value.push(val[index])
return
}
//三元表达式 数组的长度是否为true(0位假值) 如果是 使用reduce方法进行累加,如果不是就为0
//判断当前数组长度是否0,如果不是就使用reduce计算累加值,如果为0,高度就为0
const heightTotal_1 = heightArry_1.length ? Array.from(heightArry_1).reduce(( acc, cur ) => acc + cur) : 0 // 第一列的总高度
const heightTotal_2 = heightArry_2.length ? Array.from(heightArry_2).reduce(( acc, cur ) => acc + cur) : 0 // 第二列的总高
// 找到最小值
let minNumber = Math.min(heightTotal_1,heightTotal_2)
switch (minNumber) {
case heightTotal_1:
heightArry_1.push(item.offsetHeight)
arr1.value.push(val[index])
break
case heightTotal_2:
heightArry_2.push(item.offsetHeight)
arr2.value.push(val[index])
break
}
})
// 重新将数据赋值给各列数组
cardList1.value = arr1.value
cardList2.value = arr2.value
}
function move(){
let page = Math.abs(document.querySelector('.page-main').scrollTop) //盒子的超出滚动距离
let boxheight = document.querySelector('.page-main').clientHeight//盒子的高
let height = document.querySelector('.coloum1').scrollHeight//列表1的滚动高度
let height2 = document.querySelector('.coloum2').scrollHeight//列表2的滚动高度
let minheight = Math.min(height,height2) //取最列表1与列表2高度的小值
//如果滚动高度+盒子高大于最小的列表高时相当于相对矮的列表触底
if(page+boxheight>minheight){
equallyCard(cardLis)
caLFlex(cardLis)
}
}
</script>
<style lang="scss" scoped>
.page-main{
background: #ffffff;
height:400px;
overflow: scroll;
padding: 0 30px;
.card{
.card-item{
visibility: hidden;
margin-bottom: 20px;
text-align: center;
width: 200px;
margin-right: 10px;
border-radius: 16px;
}
.visibles {
visibility: visible;
}
}
}
.left{
float: left;
}
.right{
float: right;
}
</style>