美文网首页
瀑布流布局

瀑布流布局

作者: WikiPine | 来源:发表于2023-10-06 15:01 被阅读0次

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

    相关文章

      网友评论

          本文标题:瀑布流布局

          本文链接:https://www.haomeiwen.com/subject/rxopbdtx.html