You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Vue3实现按行排列瀑布流布局问题求助

如何实现按行排列的瀑布流区块?

我需要创建按行排列的瀑布流区块,虽然知道要用v-for渲染元素,但不知道具体实现方式。试了@yeger/vue-masonry-wall库,它只能实现按列排列的瀑布流,满足不了需求。

我的现有代码如下:

<template>
  <masonry-wall 
    :items="items" 
    :column-width="200" 
    :gap="16" 
    :ssr="false"
    class="masonry-wall"
  >
    <template #default="{ item }">
      <div class="masonry-item">
        <img :src="item.img" :alt="item.title" class="masonry-img" />
        <div class="masonry-caption">
          <h1 class="masonry-title">{{ item.title }}</h1>
          <span class="masonry-description">{{ item.description }}</span>
        </div>
      </div>
    </template>
  </masonry-wall>
</template>

<script setup>
const items = [
  { img: 'link-to-image1.jpg', title: 'LEGO夜灯', description: '造型别致的LEGO夜灯' },
  { img: 'link-to-image2.jpg', title: 'LEGO夜灯', description: '儿童房的理想选择' },
  { img: 'link-to-image3.jpg', title: 'LEGO夜灯', description: '明亮又时尚' },
  { img: 'link-to-image4.jpg', title: 'LEGO夜灯', description: '耐用设计' },
  { img: 'link-to-image5.jpg', title: 'LEGO夜灯', description: '节能省电' },
  { img: 'link-to-image6.jpg', title: 'LEGO夜灯', description: '绝佳礼物' },
];
</script>

方案1:用CSS Grid实现按行瀑布流(推荐)

不需要依赖第三方库,直接用CSS Grid的auto-flow: row dense就能实现按行填充、自动补位的瀑布流效果:

<template>
  <div class="row-masonry-container">
    <div v-for="(item, index) in items" :key="index" class="masonry-item">
      <img :src="item.img" :alt="item.title" class="masonry-img" />
      <div class="masonry-caption">
        <h1 class="masonry-title">{{ item.title }}</h1>
        <span class="masonry-description">{{ item.description }}</span>
      </div>
    </div>
  </div>
</template>

<script setup>
const items = [
  { img: 'link-to-image1.jpg', title: 'LEGO夜灯', description: '造型别致的LEGO夜灯' },
  { img: 'link-to-image2.jpg', title: 'LEGO夜灯', description: '儿童房的理想选择' },
  { img: 'link-to-image3.jpg', title: 'LEGO夜灯', description: '明亮又时尚' },
  { img: 'link-to-image4.jpg', title: 'LEGO夜灯', description: '耐用设计' },
  { img: 'link-to-image5.jpg', title: 'LEGO夜灯', description: '节能省电' },
  { img: 'link-to-image6.jpg', title: 'LEGO夜灯', description: '绝佳礼物' },
];
</script>

<style scoped>
.row-masonry-container {
  display: grid;
  grid-template-columns: repeat(auto-fill, minmax(200px, 1fr));
  grid-auto-flow: row dense; /* 核心:按行填充,自动补位空白 */
  gap: 16px;
}

.masonry-item {
  break-inside: avoid; /* 防止元素被跨行分割 */
  border-radius: 8px;
  overflow: hidden;
  box-shadow: 0 2px 8px rgba(0,0,0,0.1);
}

.masonry-img {
  width: 100%;
  height: auto;
  display: block;
}

.masonry-caption {
  padding: 12px;
}

.masonry-title {
  font-size: 16px;
  margin: 0 0 8px;
}

.masonry-description {
  font-size: 14px;
  color: #666;
}
</style>

关键说明:

  • grid-auto-flow: row dense会让网格按行方向排列,自动填补前面行因元素高度差留下的空白
  • repeat(auto-fill, minmax(200px, 1fr))会根据容器宽度自动生成合适数量的列,保证每列至少200px宽

方案2:自定义JS计算高度(复杂场景适配)

如果需要更精细的排列控制,可以用JS计算元素高度,手动分配到行中:

<template>
  <div class="custom-masonry-container">
    <div v-for="(row, rowIndex) in arrangedRows" :key="rowIndex" class="masonry-row">
      <div v-for="(item, colIndex) in row" :key="colIndex" class="masonry-item" :style="{ height: `${item.height}px` }">
        <img :src="item.img" :alt="item.title" class="masonry-img" />
        <div class="masonry-caption">
          <h1 class="masonry-title">{{ item.title }}</h1>
          <span class="masonry-description">{{ item.description }}</span>
        </div>
      </div>
    </div>
  </div>
</template>

<script setup>
import { ref, onMounted, watch } from 'vue';

const items = ref([
  { img: 'link-to-image1.jpg', title: 'LEGO夜灯', description: '造型别致的LEGO夜灯', height: 0 },
  { img: 'link-to-image2.jpg', title: 'LEGO夜灯', description: '儿童房的理想选择', height: 0 },
  { img: 'link-to-image3.jpg', title: 'LEGO夜灯', description: '明亮又时尚', height: 0 },
  { img: 'link-to-image4.jpg', title: 'LEGO夜灯', description: '耐用设计', height: 0 },
  { img: 'link-to-image5.jpg', title: 'LEGO夜灯', description: '节能省电', height: 0 },
  { img: 'link-to-image6.jpg', title: 'LEGO夜灯', description: '绝佳礼物', height: 0 },
]);

const arrangedRows = ref([]);
const columnCount = ref(3); // 自定义列数

// 计算元素高度并按行分配
const arrangeItems = () => {
  // 模拟计算高度(实际项目中可通过getBoundingClientRect获取真实高度)
  items.value.forEach(item => {
    item.height = 200 + Math.random() * 150;
  });

  // 优先填充高度最小的列
  const rows = Array.from({ length: columnCount.value }, () => []);
  const rowHeights = Array(columnCount.value).fill(0);

  items.value.forEach(item => {
    const minHeightIndex = rowHeights.indexOf(Math.min(...rowHeights));
    rows[minHeightIndex].push(item);
    rowHeights[minHeightIndex] += item.height;
  });

  arrangedRows.value = rows;
};

onMounted(() => {
  arrangeItems();
  window.addEventListener('resize', arrangeItems);
});

watch(items, arrangeItems);
</script>

<style scoped>
.custom-masonry-container {
  display: flex;
  flex-direction: column;
  gap: 16px;
}

.masonry-row {
  display: flex;
  gap: 16px;
}

.masonry-item {
  flex: 1;
  border-radius: 8px;
  overflow: hidden;
  box-shadow: 0 2px 8px rgba(0,0,0,0.1);
}

.masonry-img {
  width: 100%;
  height: calc(100% - 60px); /* 预留标题区域高度 */
  object-fit: cover;
  display: block;
}

.masonry-caption {
  padding: 12px;
  height: 60px;
}

.masonry-title {
  font-size: 16px;
  margin: 0 0 8px;
}

.masonry-description {
  font-size: 14px;
  color: #666;
}
</style>

关键说明:

  • 通过JS计算每个元素的高度,将元素分配到当前总高度最小的列中,实现按行填充的瀑布流
  • 适合需要自定义排列逻辑的场景,但实现复杂度高于CSS Grid方案

内容的提问来源于stack exchange,提问作者Сейтек Бегалиев

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.15 21:13:16