Grid布局实现不定高度的真瀑布流,只需几行代码

1,715 阅读2分钟

自己项目想要用瀑布流来适配pc端页面,但是发现找不到合适的方案,看了很多纯 CSS 的方案,基本都是假瀑布流,然后又看了很多 JS 实现的瀑布流库,基于 vue 的基本都没什么 star,甚至很多都是几年不维护了,随便选了一个果然也不可用,就自己又想了一个新的方案。

效果

1.png

我做了视频讲解,嫌麻烦可以直接看文章

【Grid布局实现不定高度的真瀑布流,只要几行代码】

实现

先贴核心代码实现,以下是核心代码

list 容器样式

.list {
  display: grid;
  grid-auto-rows: 5px;
  grid-template-columns: repeat(auto-fill, calc(50% - 5px));
  align-items: start;
  justify-content: space-between;
}

卡片 vue 组件的监听,其实就是当 dom 渲染之后再执行,纯 js 也一样的,只是我项目用了 vue

watch: {
  immediate: true,
  handler() {
    this.$nextTick(() => {
      const el = this.$refs.root
      const rows = Math.ceil(el.clientHeight / 5) + 2
      el.style.gridRowEnd = `span ${rows}`
    })
  },
}

就这么简单,接下来我会讲解原理

基于 Grid 布局实现瀑布流

Grid 布局我们知道他是像 Excel 表格一样一行一行的,使用 grid 布局是无法让不定高度的项目依次堆叠在一起的。 这里我们首先将 grid 网格设置的特别密集,通过 grid-auto-rows: 5px; 来将每一行网格设置为 5px 高度

2.png

然后我们使用 align-item: start 让项目不会被自动拉伸,因为后续我们需要获取到他真实的高度。 然后我们在 dom 渲染后执行一些操作,这里我使用的是 vue,所以监听卡片数据 info 发生变化,然后 nextTick 保证 dom 已经渲染出来,之后我们首先获取到卡片的 dom,然后使用 clientHeight 来获取卡片的真实高度。 我们用这个真实高度除以5,也就是网格一行的高度,就得到了卡片占了多少行了。这里加上2是为了两个卡片之间的堆叠有10px的间距,不然都挤在一起了。 最后我们为卡片添加内联样式 grid-row-end: span 行数,这样卡片就得到了适合他高度的网格数。

3.png

如何保证排序是正确的

grid 布局使用 grid-row-end: span 行数 来排布时,是浏览器自动排序,我们只是告诉浏览器这个卡片占了多少格,而没有限定死他的位置,浏览器会自动堆叠,并且是真正的哪有坑就往哪堆,并不是左右左右的固定顺序,因此实现效果完全是真瀑布流的效果。 因为 js 做的事情非常少,堆叠排布和元素宽度变化都是浏览器自动处理的,因此性能损失很少