瀑布流布局(Waterfall Flow Layout)是一种常用于网页设计的布局方式,它可以实现图片或内容的自适应排列,使页面看起来更加美观和流畅。
先看实现效果:
下面是使用 vue 实现瀑布流布局的一般步骤:
1.定义列数、行间距、列间距
2.容器设置为相对定位、item设置为绝对定位
3.获取容器宽度:容器总宽度-内边距(paddingLeft+paddingRight)
4.计算每列宽度:(容器宽度 - 列间距 * (列数 - 1)) / 列数
5.获取每一个item的高度(需要放在nextTick里执行)
6.根据列数生成一个记录item高度的数组columnHeights,数组长度和列数相同,默认填充为 0
7.遍历所有item:计算每个item的left、top
8.container高度:columnHeights中的最大值
代码实现:
<template> <h1 class="h-[50px] text-3xl text-center my-10">Vue3 Waterfall Flow Layout</h1> <div class="text-center mb-10"> 列数 <input v-model.number="column" type="number" class="outline-none border border-zinc-800 rounded px-2 py-0.5" /> </div> <div ref="containerRef" class="px-10 md:px-20 lg:px-40 xl:px-60 relative" :style="{ height: containerHeight + 'px' }" > <div v-for="item in data" :key="item.url" class="item absolute duration-300" :style="{ width: columnWidth + 'px' }" > <img :src="item.url" alt="" :style="{ height: (columnWidth / item.width) * item.height + 'px' }" class="rounded" /> </div> </div> </template> <script setup lang="ts"> import { ref, onMounted, nextTick, watch } from 'vue' // data 是一个数组,存放图片 url 和宽高,格式为:{ url: 'https://xxxx', width: xx, height: xx } import data from './data' const column = ref(4) const rowSpacing = ref(20) const columnSpacing = ref(20) const containerRef = ref<HTMLElement | null>(null) // 容器总宽度 const containerWidth = ref(0) // 容器总高度 const containerHeight = ref(0) // 列宽 = (容器宽度 - 列间距 * (列数 - 1)) / 列数 const columnWidth = ref(0) const containerLeft = ref(0) // 计算容器宽度 const useContainerWidth = () => { const { paddingLeft, paddingRight } = window.getComputedStyle(containerRef.value!) containerLeft.value = parseFloat(paddingLeft) containerWidth.value = containerRef.value!.clientWidth - parseFloat(paddingLeft) - parseFloat(paddingRight) } // 计算列宽 const useColumnWidth = () => { columnWidth.value = (containerWidth.value - columnSpacing.value * (column.value - 1)) / column.value } // 获取最小高度 const getMinHeight = (arr: number[]) => { return Math.min(...arr) } // 获取最小高度的索引 const getMinHeightIndex = (arr: number[]) => { return arr.indexOf(getMinHeight(arr)) } // 获取所有图片的高度 const itemHeights = ref<number[]>([]) const useItemHeight = () => { const allItems = document.querySelectorAll<HTMLElement>('.item') itemHeights.value = Array.from(allItems).map((item) => item.clientHeight) } const columnHeights = ref(Array(column.value).fill(0)) const getItemLeft = () => { const column = getMinHeightIndex(columnHeights.value) return (columnWidth.value + columnSpacing.value) * column + containerLeft.value } const getItemTop = () => { return getMinHeight(columnHeights.value) } const increaseColumnHeight = (index: number) => { const minHeightColumnIndex = getMinHeightIndex(columnHeights.value) columnHeights.value[minHeightColumnIndex] += itemHeights.value[index] + rowSpacing.value } // 计算每个 item 的位置 const useItemPosition = () => { const allItems = document.querySelectorAll<HTMLElement>('.item') allItems.forEach((item, index) => { item.style.left = getItemLeft() + 'px' item.style.top = getItemTop() + 'px' increaseColumnHeight(index) }) containerHeight.value = Math.max(...columnHeights.value) } onMounted(() => { useContainerWidth() useColumnWidth() nextTick(() => { useItemHeight() useItemPosition() }) }) watch(column, (value) => { columnHeights.value = Array(value).fill(0) useColumnWidth() nextTick(() => { useItemHeight() useItemPosition() }) }) </script>
复制