网上也有许多瀑布流的介绍,纯CSS或JS版的。
纯CSS
基本上不太靠谱,理由是 CSS无法处理图片高度不固定问题,但CSS博大精深,这边持保留意见,如果有,那么CSS肯定是最优解。
JS版
目前已有的资料大多数都是 DOM结合,样式混在JS代码中,从目前大多数以数据驱动为核心的写法上来看,不太优雅,下面给出改造后的Vue3版本的瀑布流布局代码。
核心代码
// 声明
const imgHeights = ref([]);
const imgItemList = ref([]);
const imgOriginObj = ref({});
// 计算图片的高度并保存,失败则统计为0
let count = 0;
let originData = [
// 图片资源数组
];
const originLength = originData.length;
originData.forEach(val=>{
let img = new Image();
img.src = val;
img.onload = img.onerror = (e)=>{
count++;
if(e.type === 'load') {
imgOriginObj.value[val] = img.height;
} else {
imgOriginObj.value[val] = 0;
}
// 加载完最后一张图片后进行数据排序集合
if(count === originLength) {
resolveImageList(originData);
}
}
})
// 图片数据集合排序处理
const resolveImageList = (originData) => {
originData.forEach(val=>{
let index = getMinHeightIndex();
imgHeights.value[index] = imgHeights.value[index] + parseInt(imgOriginObj.value[val] ?? 0);
imgItemList.value[index].push(val);
})
}
// 获取最小高度的index
const getMinHeightIndex = () => {
let index = 0;
let minNum = imgHeights.value[0];
imgHeights.value.forEach((val, key)=>{
if(val < minNum) {
minNum = val;
index = key;
}
})
return index;
}
可以看出,其实利用的就是 图片预加载获取高度 然后计算存放的位置,上面瀑布流的渲染顺序是固定的,只要数据固定,那么渲染的页面就是固定
完整代码
<style lang="scss" scoped>
.work-container{
display: flex;
gap: 10px;
}
.work-item{
flex: 1;
}
.work-item-img{
margin-top: 10px;
border-radius: 10px;
width: 100%;
display: block;
}
</style>
<template>
<div class="work-container">
<div class="work-item" v-for="(item, index) in imgItemList" :key="index">
<img class="work-item-img" v-for="url in item" :key="url" :src="url" />
</div>
</div>
</template>
<script setup>
const imgHeights = ref([]);
const imgItemList = ref([]);
const imgOriginObj = ref({});
onMounted(() => {
initDataStructure();
loadImage();
listenHandleScroll();
})
// 滚动监听
const listenHandleScroll = () => {
window.onscroll = () => {
let scrollTop = document.documentElement.scrollTop || document.body.scrollTop;
let scrollHeight = document.documentElement.scrollHeight || document.body.scrollHeight;
let clientHeight = document.documentElement.clientHeight || document.body.clientHeight;
if (scrollTop + clientHeight >= scrollHeight - 200) {
// loadImage();
}
}
}
// 初始化数据结构,依据宽度自动计算
const initDataStructure = () => {
const clientWidth = document.documentElement.clientWidth || document.body.clientWidth;
let num = 4, itemArr = [], heightArr = [];
if(clientWidth > 1200) {
num = 4;
} else if(clientWidth > 800) {
num = 3;
} else {
num = 2;
}
for(let i = 0; i < num; i++) {
itemArr.push([]);
heightArr.push(0);
}
imgItemList.value = itemArr;
imgHeights.value = heightArr;
imgOriginObj.value = {};
}
// 加载图片数据
const loadImage = () => {
let originData = [
// 图库资源自行添加
];
// 计算图片的高度并保存,失败则统计为0
let count = 0;
const originLength = originData.length;
originData.forEach(val=>{
let img = new Image();
img.src = val;
img.onload = img.onerror = (e)=>{
count++;
if(e.type === 'load') {
imgOriginObj.value[val] = img.height;
} else {
imgOriginObj.value[val] = 0;
}
// 加载完最后一张图片后进行数据排序集合
if(count === originLength) {
resolveImageList(originData);
}
}
})
}
// 图片数据集合排序处理
const resolveImageList = (originData) => {
originData.forEach(val=>{
let index = getMinHeightIndex();
imgHeights.value[index] = imgHeights.value[index] + parseInt(imgOriginObj.value[val] ?? 0);
imgItemList.value[index].push(val);
})
}
// 获取最小高度的index
const getMinHeightIndex = () => {
let index = 0;
let minNum = imgHeights.value[0];
imgHeights.value.forEach((val, key)=>{
if(val < minNum) {
minNum = val;
index = key;
}
})
return index;
}
</script>
可优化点
1 自适应上,大小屏幕切换不会自动刷新,可以用resize处理一下,同时数据缓存一下,提高性能
2 目前的分布算法可能不是最优解,但是应该差不多了,没那么多数据,有数据了再优化
网友评论