简介
使用Vue
、TailwindCSS
封装的前端瀑布流式布局组件,可指定列数,自动计算平衡列
使用方法
<WaterfallLayout :columnCount="3" :items="items">
<template #item="{ item }">
{{ item }}
</template>
</WaterfallLayout>
代码
文件名:WaterfallLayout.vue
<template>
<div class="relative flex w-full" :style="{ gap: horizontalGap }" ref="containerRef">
<div
v-for="(column, columnIndex) in columns"
:key="columnIndex"
class="flex flex-col"
:style="{
width: `${100 / props.columnCount}%`,
gap: verticalGap
}"
>
<div v-for="item in column" :key="item.id" class="w-full" ref="itemRefs">
<slot name="item" :item="item.data" />
</div>
</div>
</div>
</template>
<script setup lang="ts">
import { ref, onMounted, watch, computed } from 'vue'
interface WaterfallItem<T> {
id: number
height: number
data: T
}
const props = withDefaults(
defineProps<{
columnCount: number
items: any[] // 传入的数据列表
horizontalGap?: string
verticalGap?: string
}>(),
{
horizontalGap: '1rem',
verticalGap: '1rem'
}
)
const horizontalGap = computed(() => props.horizontalGap)
const verticalGap = computed(() => props.verticalGap)
const containerRef = ref<HTMLElement | null>(null)
const itemRefs = ref<HTMLElement[]>([])
const columns = ref<WaterfallItem<any>[][]>([])
// 初始化列数组
const initializeColumns = () => {
columns.value = Array(props.columnCount)
.fill(null)
.map(() => [])
}
// 找到最短的列
const findShortestColumn = () => {
const columnHeights = columns.value.map((column) => {
return column.reduce((height, item) => height + item.height, 0)
})
return columnHeights.indexOf(Math.min(...columnHeights))
}
// 处理项目分布
const arrangeItems = () => {
initializeColumns()
props.items.forEach((data, index) => {
const height = itemRefs.value[index]?.offsetHeight ?? 0
const item: WaterfallItem<any> = {
id: index,
height,
data
}
const shortestColumnIndex = findShortestColumn()
columns.value[shortestColumnIndex].push(item)
})
}
// 监听数据变化
watch(() => props.items, arrangeItems, { deep: true })
watch(() => props.columnCount, arrangeItems)
// 监听容器大小变化
const resizeObserver = new ResizeObserver(() => {
arrangeItems()
})
onMounted(() => {
if (containerRef.value) {
resizeObserver.observe(containerRef.value)
}
arrangeItems()
})
</script>