瀑布流布局

2023-10-06  本文已影响0人  WikiPine

网上也有许多瀑布流的介绍,纯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 目前的分布算法可能不是最优解,但是应该差不多了,没那么多数据,有数据了再优化

上一篇 下一篇

猜你喜欢

热点阅读