vue3 实现简单瀑布流

17 篇文章 0 订阅

一、整理思路

  • 实际场景中,瀑布流一般由 父组件 提供 数据列表,子组件渲染
  • 每个图片都是根据容器进行 绝对定位 ,从而定好自己的位置
  • 取出 屏幕的宽度,设定 图片的宽度 固定 为一个值,计算可以铺 多少列
  • 按列数 先铺上第一行lefttop 值)
  • 第一行铺满以后,后面的每一张都要 铺在高度最低的列,这是瀑布流的核心!所以要维护一个 长度为 列数 的 列高度数组 用于比较
  • 总结:每次放置一张图,都要计算它的 lefttop 值丢进瀑布流数组中,并且更新 列高度数据 便于下次对比

二、代码

父组件

<WaterFall :list="list" />
// 模拟列表数据
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: "gray",
  },
  {
    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: "skyblue",
  },
  {
    height: 120,
    background: "#33CCFF",
  },
  {
    height: 400,
    background: "#999966",
  },
  {
    height: 630,
    background: "#CC9966",
  },
  {
    height: 250,
    background: "#33FF00",
  },
  {
    height: 300,
    background: "yellow",
  },
  {
    height: 500,
    background: "green",
  },
];

子组件

<template>
  <div class="list">
    <div
      class="item"
      v-for="(item, index) in waterList"
      :style="{
        width: width + 'px',
        height: item.height + 'px',
        left: item.left + 'px',
        top: item.top + 'px',
        background: item.background,
      }"
    >
      {{ index }}
    </div>
  </div>
</template>


<script setup lang="ts">
import { ref, reactive, onMounted } from "vue";

const props = defineProps<{
  list: any[];
}>();

// 图片宽度
const width = 120;
// 图片上下间距
const gap = 20;
// 瀑布流数组
const waterList = ref<any[]>([]);
// 列高度数组
const heightList = reactive<number[]>([]);

// 屏幕宽度需要在 mounted 之后拿到
onMounted(() => {
  // 计算列数
  const column = Math.floor(document.body.clientWidth / width);

  // 核心内容就是维护每个图片的 left、top
  for (let i = 0; i < props.list.length; i++) {
    // 先铺上第一行(i < column 则表示是第一行)
    if (i < column) {
      props.list[i].top = 0;
      props.list[i].left = width * i;
      // 塞进瀑布流
      waterList.value?.push(props.list[i]);
      // 高度数据更新
      heightList[i] = props.list[i].height;
    }

    // 后面的就要一张张塞进去,每次找出最低的列往里塞
    else {
      // 最低的高度,先默认为第一列高度
      let current = heightList[0];
      // 最低的列,先默认为第一个
      let col = 0;

      // 循环每一列进行比较
      heightList.forEach((h, i) => {
        if (h < current) {
          current = h;
          col = i;
        }
      });
      console.log("最低的列", col, "高度为", current);

      // 由此计算出该图片的 left、top
      props.list[i].left = col * width;
      props.list[i].top = current + gap;
      // 塞进瀑布流
      waterList.value.push(props.list[i]);

      // 更新列高度数组
      heightList[col] = current + gap + props.list[i].height;
    }
  }
  console.log("waterList", waterList.value);
  console.log("heightList", heightList);
});
</script>
<style lang="scss" scoped>
.list {
  position: relative;
  height: 100%;
  overflow: auto;
  
  .item {
    position: absolute;
    font-size: 30px;
  }
}
</style>

效果如图

在这里插入图片描述

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值