基于 Vue 3 的瀑布流布局实现(AI依据代码生成 Blog)
背景
瀑布流布局是一种广泛应用于图片、内容展示的方式,尤其在展示动态加载内容时。本文将基于 Vue 3 实现一个简易的瀑布流布局,支持动态加载和随机生成图片。
代码实现
1. 项目结构
组件模板采用 Vue 3 的 <script setup> 形式实现。主要部分包括:
- 模板结构:定义基本的瀑布流布局框架。
- 脚本逻辑:计算列数、动态加载内容、监测滚动事件。
- 样式设计:通过
CSS 实现列和单元的布局。
2. 代码详解
模板部分
<template> <div id="container" ref="container"> <slot name="empty"></slot> <div class="container-content"> <div class="column-item" v-for="i in getColumnCount" :key="i"></div> </div> <div id="load-more">加载更多...</div> </div> </template> <script setup lang="ts"> import { ref, reactive, computed, onMounted } from "vue";
const container = ref(); // 瀑布流容器 const pageOptions = reactive({ page: 1, // 当前页码 size: 20, // 每次加载的数据量 }); const results = ref([]); // 存储加载的数据 const props = defineProps({ data: { type: Array, required: true }, columnWidth: { type: Number, default: 200 }, columnSpace: { type: Number, default: 10 }, rowSpace: { type: Number, default: 10 }, });
// 动态计算列数 const getColumnCount = computed(() => { const w = container.value?.clientWidth; return w ? Math.floor( (w + props.columnSpace) / (props.columnWidth + props.columnSpace) ) : 0; });
// 随机生成图片数据 function getItems(len) { const items: any = []; for (let i = 0; i < len; i++) { const width = Math.floor(Math.random() * (400 - 300 + 1) + 300); const height = Math.floor(Math.random() * (500 - 250 + 1) + 250); let url = ` data:image/svg+xml;charset=utf-8, <svg xmlns="http://www.w3.org/2000/svg" width="${width}" height="${height}"> <text x="50%" y="50%" dominant-baseline="middle" text-anchor="middle"> ${width} * ${height} </text> </svg>`; items.push(url.trim()); } return items; }
// 加载数据 function loadMore() { setTimeout(() => { const res = getItems(pageOptions.size); results.value = results.value.concat(res);
res.forEach((element, index) => { const newDom = putDom(element, index + pageOptions.size * (pageOptions.page - 1)); requestAnimationFrame(() => { const short = getTheShortOne(); short?.appendChild(newDom); }); }); pageOptions.page += 1; }, 200); }
// 将图片数据插入 DOM function putDom(url, index) { const div = document.createElement("div"); const img = document.createElement("img"); const text = document.createElement("div"); div.appendChild(img); div.appendChild(text); div.className = "cell-item"; img.src = url; text.textContent = index + 1; return div; }
// 找到最短列 function getTheShortOne() { const columnItems = document.querySelectorAll(".column-item"); if (columnItems.length === 0) return; let minElement = columnItems[0]; let minHeight = minElement.clientHeight; columnItems.forEach((item) => { const itemHeight = item.clientHeight; if (itemHeight < minHeight) { minHeight = itemHeight; minElement = item; } }); return minElement; }
// 添加滚动监听 function addObserve() { const observe = new IntersectionObserver((entries) => { entries.forEach((entry) => { if (entry.isIntersecting && results.value.length > 0) { loadMore(); } }); }); const dom = document.getElementById("load-more"); if (dom) observe.observe(dom); }
onMounted(() => { loadMore(); addObserve(); }); </script> <style lang="scss"> .container { overflow-y: auto; position: relative; } .container-content { --column-gap: 15px; --column-width: 200px; --row-space: 15px;
display: flex; gap: 0 var(--column-gap); width: 100%; justify-content: center;
.column-item { width: var(--column-width); display: flex; flex-direction: column; gap: var(--row-space) 0; height: fit-content; }
.cell-item { font-size: 16px; font-weight: 500; display: flex; flex-direction: column;
img { width: 100%; object-fit: cover; background-color: grey; }
div { margin-top: 14px; line-height: 18px; } } } #load-more { text-align: center; height: 50px; display: flex; align-items: center; justify-content: center; } </style>
|
待优化点
- dom复杂时的高度计算问题(最好是,计算高度,缓存高度)
- 大量数据, 内存压力问题(虚拟滚动的支持)
总结
本文通过 Vue 3 和原生 JS 实现了一个功能完整的瀑布流组件,适合图片和内容流的展示场景。实现了动态加载、随机图片生成,以及高效的布局调整。