史上最全的瀑布流布局前端方案汇总!

9,523 阅读6分钟

什么是瀑布流布局

瀑布流又称瀑布流式布局,是一种比较流行的页面布局方式,专业的英文名称为[Masonry Layouts]。与传统的分页显示不同,视觉表现为参差不齐的多栏布局,最早是由Pinterest首先运用。

瀑布流布局的优点

  • 节省空间,外表美观,更有艺术性。

  • 对于触屏设备非常友好,通过向上滑动浏览

  • 用户浏览时的观赏和思维不容易被打断,留存更容易。

瀑布流布局的缺点

  • 用户无法了解内容总长度,对内容没有宏观掌控。
  • 用户无法了解现在所处的具体位置,不知道离终点还有多远。
  • 回溯时不容易定位到之前看到的内容。
  • 容易造成页面加载的负荷。
  • 容易造成用户浏览的疲劳,没有短暂的休息时间。

瀑布流布局适用场景

  • 内容以图片为主的时候。图片占用空间比较大,并且大脑理解的速度相比理解文字要快,短时间内可以扫过的内容很多,所以如果用分页显示的话用户务必会频繁的翻页,影响沉浸式的体验,而瀑布流可以解决这个问题。

  • 信息与信息之间相对独立时。如果信息关联性强,用户务必会进行大量的回溯操作去查看之前或者之后的信息,相反,如果信息相对独立的话,可以使用瀑布流,让用户同时接受来自不同地方的信息。

  • 信息与搜索匹配比较模糊时。瀑布流给人的直观印象,就是同时显示的信息与用户搜索的匹配度大致一样,而分页显示的直观印象则是越靠上的信息被认为与用户的搜索越匹配。因此,当信息与搜索匹配度没有明显区分度时,可以采用瀑布流。

  • 用户目的性不强的时候。如果用户有特定需要查找的信息,分页查找定位更方便,而当目的性较弱的时候,瀑布流可以增加用户停留的时间和意想不到的收获。

瀑布流布局前端技术方案

先说结论,实际工程中推荐直接使用第三方库。

1. css

1.1 multi-column 多栏布局

multi-column实现瀑布流主要依赖以下几个属性:

  • column-count: 设置共有几列
  • column-width: 设置每列宽度,列数由总宽度每列宽度计算得出
  • column-gap: 设置列与列之间的间距

column-countcolumn-width都可以用来定义分栏的数目,而且并没有明确的优先级之分。优先级的计算取决与具体的场景。

计算方式为:计算column-countcolumn-width转换后具体的列数,哪个小就用哪个。

.masonry{
    column-count: 3;
    column-gap: 10px;
}
.masonry .item{
    border:1px solid #999;
    margin-bottom: 10px;
}
.masonry .item img{
    width: 100%;
}

我们可以看到,虽然实现了瀑布流的效果,但奇怪的是例子中前两列的最后一个元素的文本内容自动断开,一部分在当前列尾,一部分在下一列的列头。

而这种展示方式无疑是我们不希望看到的,我们希望的是每个元素都是独立的,前后不断开,此时我们需要使用break-inside来实现。

break-inside: auto | avoid

  • auto: 元素可以中断
  • avoid: 元素不能中断

修改一下之前的例子:

.masonry .item{
    break-inside: avoid;
}

但由于multi-column布局中子元素的排列顺序是先从上往下从左至右,所以这种方式仅适用于数据固定不变的情况,对于滚动加载更多等可动态添加数据的情况就并不适用了。

1.2 grid 布局实现瀑布流

网格布局(Grid)是最强大的 CSS 布局方案。

它将网页划分成一个个网格,可以任意组合不同的网格,做出各种各样的布局。以前,只能通过复杂的 CSS 框架达到的效果,现在浏览器内置了。

关于grid语法可以参照阮一峰的博客CSS Grid 网格布局教程

.wrap-waterfall--grid img{vertical-align: top;width: 100px}
.wrap-waterfall--grid .list{
    display: grid;
    grid-gap: 10px;
    /* 可以看到,网格大小,占据位置是需要提前设定的 */
    grid-template-columns: repeat(4, 1fr);
    grid-auto-rows: minmax(50px, auto);
}

网传有 gird 实现瀑布流布局的方案,但是我看了几个他们不是色块,就是图片变形、裁剪,方案是用 nth-child 定高,所以这个方案有局限性,只适用于固定布局,如下图布局,

,显然不满足瀑布流大部分的使用场景。

1.3 Flexbox 实现瀑布流

关于flex语法可以参照阮一峰的博客Flex 布局教程:语法篇

flex布局默认情况下是水平排列,可以修改为垂直排列并且允许换行达到纵向瀑布流的效果。

flex-flow: column wrap;
height: 2300px;

局限性:必须用固定高度使内容换行,填充比较难以控制;不固定高度的话要结合js才能实现,如下:

<template>
  <div class="masonry">
    <div class="colmun">
      <img class="item" :src="i.img" :key="i.id" v-for="i in data1">
    </div>
    <div class="colmun">
      <img class="item" :src="i.img" :key="i.id" v-for="i in data2">
    </div>
    <div class="colmun">
      <img class="item" :src="i.img" :key="i.id" v-for="i in data3">
    </div>
  </div>
</template>


<script>
import data from "./data.json";

export default {
  data() {
    let data1 = [], //第一列
      data2 = [], //第二列
      data3 = [], //第三列
      i = 0;

    while (i < data.length) {
      data1.push(data[i++]);
      if (i < data.length) {
        data2.push(data[i++]);
      }
      if (i < data.length) {
        data3.push(data[i++]);
      }
    }
    return {
      //第一列
      data1,
      //第二列
      data2,
      //第三列
      data3
    };
  }
};
</script>

<style lang="scss" scoped>
.masonry {
  display: flex;
  flex-direction: row;
  .colmun {
    display: flex;
    flex-direction: column;
    flex: 1;
    padding: 0 2px;
    .item {
      margin-bottom: 5px;
      width: 100%;
    }
  }
}
</style>

1.4 grid-template-rows: masonry

看了以上的各种css方案,都有各自的弊端,实际使用场景一般也不会通过纯css做瀑布流布局。

CSS 新属性 grid-template-rows: masonry 轻松实现瀑布流布局,一行代码即可搞定(目前在 FirefoxNightly 可用),看了caniuse,兼容性感人。

后续如果所有浏览器都支持的话,今天写的这篇文章就可以归档了。

2. 原生js

自己实现个瀑布流布局相对比较繁琐,网上的各种js解决方案写法各式各样,不过思路都类似,下面提供一种解决方案供参考:

  1. 确定每行放几张图片, 每行的个数(column)=页面宽度(pageWidth)/(图片盒子宽度+图片间距)

  2. 确定一行多少个之后首先需要将第一行排列好 (绝对定位的方式,使用js排列好)

  3. 找出每一行的最小高度,排列完每一张图片之后更新最小高度

3.第三方库

第三方库才是前端攻城狮智慧的结晶,实际工程中我使用了比较小众的@egjs/vue-infinitegrid,这是egjs-infinitegrid的使用文档,有各类框架的具体使用姿势,涵盖了各种使用场景。

网上还有各类第三方库,比如 vue-waterfall,感兴趣的可以自行尝试。

总结

纯css实现有各自的弊端,兼容性问题不少;

js实现需要评估时间成本,并且可能有未知bug;

实际工程中使用第三方库才是最稳妥的解决方案。