美文网首页
Flex布局,几行代码就可以实现瀑布流布局,代码简单,定制化强。

Flex布局,几行代码就可以实现瀑布流布局,代码简单,定制化强。

作者: 哈叽哈叽叽歪歪 | 来源:发表于2024-01-31 09:11 被阅读0次

    原理很简单,计算图片的宽高,再计算每列的使用高度,然后再将当前图片放置在列高最小的一列。其实这种方式使用什么方式布局都无所谓,我使用的是flexd布局。Flex的使用在这里就不讲解了,网上的教程一大堆。这里讲解使用VUE3实现,并封装成可以使用的组件。

    话不多说,上效果


    h5-editor

    以下是核心代码。

    template

    <template>
        <div :id="fluidId" v-infinite-scroll="loadMore" :infinite-scroll-distance="20" :infinite-scroll-immediate="false" class="uabs uof-x uof-y-s">
            <div v-if="imgList.length > 0" class="ub ub-f1 upad-rl06">
                <div v-for="i in col" :key="i" class="ub ub-f1 ub-ver" :style="'margin-left:' + (i != 1 ? gutter : 0) + 'px'">
                    <el-image v-for="img, idx in imgList[i - 1]" @click="emit('getData', img)" @dragstart="emit('dragstart', img)" @dragend="emit('dragend', img)" :key="idx" :src="img.url || img.thumbnailUrl" loading="lazy" fit="scale-down" class="img-hover uba ushadow" :style="'margin-top:' + gutter + 'px'">
                        <template #error>
                            <el-icon class="ub ub-ac ub-pc ub-fv ub-fh uc-font-gray2 uf-s2" style="height: 50px;">
                                <Picture />
                            </el-icon>
                        </template>
                    </el-image>
                </div>
                <div class="uhide">
                    <el-image v-for="img, idx in data" :key="idx" :src="img.url || img.thumbnailUrl" @load="load(img)" @error="error(img)" fit="scale-down"></el-image>
                </div>
            </div>
            <el-empty v-if="data.length==0 && isReturn" :image-size="100"></el-empty>
            <div v-else class="ub ub-ac ub-pc upad-t1">
                <el-button v-if="hasMore" type="info" link :loading="!loadOver" @click="loadMore">
                    {{ loadOver ? 'more' : 'loading' }}
                    <el-icon v-show="loadOver">
                        <ArrowDownBold />
                    </el-icon>
                </el-button>
                <el-divider v-else><span class="uf-s06 uc-font-gray1">No more</span></el-divider>
            </div>
        </div>
    </template>
    

    以上代码的主要部分是隐藏图片加载,计算当前图片的宽高。

    javascript

    <script setup>
    import {
        ref,
        onMounted
    } from 'vue';
    import { ArrowDownBold } from '@element-plus/icons-vue';
    const emit = defineEmits(['loadMore', 'getData', 'dragstart']);
    
    const props = defineProps({
        col: {
            type: Number,
            default: 2
        },
        gutter: {
            type: Number,
            default: 10
        },
        data: {
            type: Array,
            default: []
        },
        hasMore: {
            type: Boolean,
            default: false
        },
        isReturn: {
            type: Boolean,
            default: false
        }
    });
    const fluidId = ref(new Date().getTime());
    let colWidth = 0;
    let imgTotalHeight = [];
    const imgList = ref([]);
    let loadCount = 0;
    const loadOver = ref(false);
    const load = (img) => {
        if (colWidth == 0) {
            const dom = document.getElementById(fluidId.value);
            colWidth = (dom.clientWidth / props.col) - (props.col - 1) * props.gutter;
        }
        const temImg = new Image();
        temImg.src = img.url || img.thumbnailUrl;
        
        temImg.onload = function () {
            const width = temImg.width;
            const height = temImg.height;
            const minTotalHeight = Math.min(...imgTotalHeight);
            const colIdx = imgTotalHeight.indexOf(minTotalHeight);
            img.width = width;
            img.height = height;
            imgTotalHeight[colIdx] += (height * colWidth) / width;
            imgList.value[colIdx].push(img);
            loadCount++
            if (loadCount == props.data.length) {
                loadOver.value = true;
            }
        }
    }
    const error = (img) => {
        const height = 50;
        const minTotalHeight = Math.min(...imgTotalHeight);
        const colIdx = imgTotalHeight.indexOf(minTotalHeight);
        imgTotalHeight[colIdx] += height;
        imgList.value[colIdx].push(img);
        loadCount++;
        if (loadCount == props.data.length) {
            loadOver.value = true;
        }
    }
    
    const loadMore = () => {
        if (props.hasMore) {
            loadOver.value = false;
            emit('loadMore', true);
        }
    }
    
    const init = () => {
        loadCount = 0;
        loadOver.value = false;
        imgTotalHeight = [];
        imgList.value = [];
        for (let i = 0; i < props.col; i++) {
            imgTotalHeight.push(0);
            imgList.value.push([]);
        }
    }
    defineExpose({ init });
    
    onMounted(() => {
        init();
    })
    </script>
    

    以上代码核心代码为load,计算图片的宽高,再计算每列的高度,将图片放置在较矮的那一列。

    接收的Props
    col:展示的列数
    gutter: 每列之前的距离,单位px
    data: 图片列表,如:[{url:url1},{url:url2}]
    hasMore: 是否有更多,为true向下滚动可自动加载
    isReturn:展示loading用

    暴露的方法
    loadMore:加载更多
    getData:点击当前图片
    dragstart:拖拽当前图片

    style

    .ub {
      position: relative;
      display: -webkit-flex;
      display: flex;
      flex-direction: row;
    }
    .ub-ac {
      justify-content: center;
    } 
    
    .ub-pc {
      align-items: center;
    }
    .ub-fh {
      width: 100%;
    }
    
    .ub-fv {
      height: 100%;
    }
    .fluid-img {
        border-radius: 5px;
        overflow: hidden;
        min-height: 30px;
    }
    

    如何使用

    <template>
      <template v-else>
        <Fluid ref="fluidCom" :col="3" :gutter="10" :data="dataList" :is-return="isReturn" :has-more="hasMore" @load-more="getMoreMaterial"></Fluid>
      </template>
    <script setup>
      import Fluid from '@/components/Fluid.vue';
    </script>
    

    代码已开源,可参考源码。代码简单,定制化强,不防试试。
    见github

    相关文章

      网友评论

          本文标题:Flex布局,几行代码就可以实现瀑布流布局,代码简单,定制化强。

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