vue3:实现瀑布流

效果图

在这里插入图片描述

准备数据

  • 使用div模拟每个元素,准备一个数组存放每个div的高度和背景颜色
const list = [
    {
        height: 300,
        background: 'red',
    },
    {
        height: 400,
        background: 'pink',
    },
    {
        height: 500,
        background: 'blue',
    },
    {
        height: 200,
        background: 'green',
    },
    {
        height: 300,
        background: 'gray',
    },
    {
        height: 400,
        background: '#CC00FF',
    },
    {
        height: 200,
        background: 'black',
    },
    {
        height: 100,
        background: '#996666',
    },
    {
        height: 500,
        background: 'skyblue',
    },
    {
        height: 300,
        background: '#993366',
    },
    {
        height: 100,
        background: '#33FF33',
    },
    {
        height: 400,
        background: 'skyblue',
    },
    {
        height: 200,
        background: '#6633CC',
    },
    {
        height: 300,
        background: '#666699',
    },
    {
        height: 300,
        background: '#66CCFF',
    },
    {
        height: 300,
        background: 'skyblue',
    },
    {
        height: 200,
        background: '#CC3366',
    },
    {
        height: 200,
        background: '#CC9966',
    },
    {
        height: 200,
        background: '#FF00FF',
    },
    {
        height: 500,
        background: '#990000',
    },
    {
        height: 400,
        background: 'red',
    },
    {
        height: 100,
        background: '#999966',
    },
    {
        height: 200,
        background: '#CCCC66',
    },
    {
        height: 300,
        background: '#FF33FF',
    },
    {
        height: 400,
        background: '#FFFF66',
    },
    {
        height: 200,
        background: 'red',
    },
    {
        height: 100,
        background: 'skyblue',
    },
    {
        height: 200,
        background: '#33CC00',
    },
    {
        height: 300,
        background: '#330033',
    },
    {
        height: 100,
        background: '#0066CC',
    },
    {
        height: 200,
        background: 'skyblue',
    },
    {
        height: 100,
        background: '#006666',
    },
    {
        height: 200,
        background: 'yellow',
    },
    {
        height: 300,
        background: 'yellow',
    },
    {
        height: 100,
        background: '#33CCFF',
    },
    {
        height: 400,
        background: 'yellow',
    },
    {
        height: 400,
        background: 'yellow',
    },
    {
        height: 200,
        background: '#33FF00',
    },
    {
        height: 300,
        background: 'yellow',
    },
    {
        height: 100,
        background: 'green',
    },
]

实现思路

  • 计算列数colunm:固定每列的宽度width,那么列数=视口宽度/列宽
  • 盒子设置为绝对定位position: absolute;
  • 遍历数组计算每个盒子的位置:
    • 如果当前下标i小于colunm,则直接放到第i列,即divleft等于i * width
    • 如果当前下标i大于等于colunm,则需要找到高度最小的那一列,将该div放在该列上,并更新该列的高度
      • 这里可以直接用一个数组存一下每列的高度,然后每次都遍历一次数组,寻找数组高度最小值,我使用了来进行这一操作

小根堆

  • 上面说到每次都要找到高度最小的列,将div放到该列上然后再更新该列的高度,这不就是典型的堆/优先队列!
  • js实现了堆的库:fastpriorityqueue
    • 安装:npm install fastpriorityqueue
    • 导入:import FastPriorityQueue from 'fastpriorityqueue'
    • 新建堆:const topPriorityQueue = new FastPriorityQueue( (a: number[], b: number[]) => a[1] < b[1] )这里传入的函数用于自定义比较堆元素大小的方法
    • 加入元素:topPriorityQueue.add(...)
    • 取出堆顶元素:topPriorityQueue.poll()
  • 在瀑布流中,遍历数组时在下标i小于colunm时,直接把[i,list[i].top+list[i].height]放入堆中,否则,先将堆顶元素取出,将当前div放在该列,并更新其高度后放回堆中

代码

<template>
    <div class="wraps">
        <div
            v-for="(item, index) in waterList"
            :key="index"
            :style="{
                height: item.height + 'px',
                background: item.background,
                left: item.left + 'px',
                top: item.top + 'px',
            }"
            class="items"
        ></div>
    </div>
</template>
<script setup lang="ts">
import { onMounted, reactive } from 'vue'
import FastPriorityQueue from 'fastpriorityqueue'
const list = [
    {
        height: 300,
        background: 'red',
    },
    {
        height: 400,
        background: 'pink',
    },
    {
        height: 500,
        background: 'blue',
    },
    {
        height: 200,
        background: 'green',
    },
    {
        height: 300,
        background: 'gray',
    },
    {
        height: 400,
        background: '#CC00FF',
    },
    {
        height: 200,
        background: 'black',
    },
    {
        height: 100,
        background: '#996666',
    },
    {
        height: 500,
        background: 'skyblue',
    },
    {
        height: 300,
        background: '#993366',
    },
    {
        height: 100,
        background: '#33FF33',
    },
    {
        height: 400,
        background: 'skyblue',
    },
    {
        height: 200,
        background: '#6633CC',
    },
    {
        height: 300,
        background: '#666699',
    },
    {
        height: 300,
        background: '#66CCFF',
    },
    {
        height: 300,
        background: 'skyblue',
    },
    {
        height: 200,
        background: '#CC3366',
    },
    {
        height: 200,
        background: '#CC9966',
    },
    {
        height: 200,
        background: '#FF00FF',
    },
    {
        height: 500,
        background: '#990000',
    },
    {
        height: 400,
        background: 'red',
    },
    {
        height: 100,
        background: '#999966',
    },
    {
        height: 200,
        background: '#CCCC66',
    },
    {
        height: 300,
        background: '#FF33FF',
    },
    {
        height: 400,
        background: '#FFFF66',
    },
    {
        height: 200,
        background: 'red',
    },
    {
        height: 100,
        background: 'skyblue',
    },
    {
        height: 200,
        background: '#33CC00',
    },
    {
        height: 300,
        background: '#330033',
    },
    {
        height: 100,
        background: '#0066CC',
    },
    {
        height: 200,
        background: 'skyblue',
    },
    {
        height: 100,
        background: '#006666',
    },
    {
        height: 200,
        background: 'yellow',
    },
    {
        height: 300,
        background: 'yellow',
    },
    {
        height: 100,
        background: '#33CCFF',
    },
    {
        height: 400,
        background: 'yellow',
    },
    {
        height: 400,
        background: 'yellow',
    },
    {
        height: 200,
        background: '#33FF00',
    },
    {
        height: 300,
        background: 'yellow',
    },
    {
        height: 100,
        background: 'green',
    },
]
const waterList = reactive<any[]>([])
const topPriorityQueue = new FastPriorityQueue(
    (a: number[], b: number[]) => a[1] < b[1]
)
const init = () => {
    const width = 130
    const x = document.body.clientWidth
    const colunm = Math.floor(x / width)
    for (let i = 0; i < props.list.length; i++) {
        if (i < colunm) {
            waterList.push({ ...list[i], left: i * width, top: 20 })
            topPriorityQueue.add([i, waterList[i].top + waterList[i].height])
        } else {
            const [index, top] = topPriorityQueue.poll()!
            waterList.push({
                ...list[i],
                left: index * width,
                top: top + 20,
            })
            topPriorityQueue.add([index, top + waterList[i].height + 20])
        }
    }
}
onMounted(init)
</script>
<style scoped lang="scss">
.wraps {
    position: relative;
    .items {
        position: absolute;
        width: 120px;
    }
}
</style>

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值