提醒:边看代码边看文章食用效果更佳,看的时候可以自己动手试试,里面涉及到一些基本的Dom操作和InterSectionObserver API的使用,不熟悉的同学也没关系,这不是重点
废话不多说先看最后效果: 今天分享我最近看到实现瀑布流的一种方式,原文:Left-to-right Masonry Layout using CSS Grid。
我们知道用Grid可以很容易形成这样的布局:
但是由于Grid的里的每个Item是不等高的,在默认的情况下(不使用
grid-template-row或grid-auto-row),Grid会自动使用行内最高的那个元素的高作为这一行的高度,所以比较矮的Item就会留下白空隙,并不会形成紧凑的瀑布流。
思路
把整个Grid细分,然后每个Item根据它的高度span合适的行数不就可以不留下空隙了吗?见下图
听起来很美妙,但是用
grid-row还是grid-row-end属性设置span行数呢?这个span行数怎么算呢?
grid-row OR grid-row-end
grid-row是grid-row-start 和 grid-row-end的简写,所以以下两种写法是等效的,为了更精确表达我们还是用grid-row-end吧
grid-row: span 2;(因为你忽略了grid-row-start,就是告诉grid从当前位置开始span两行,包括当前位置喔)grid-row-end: span 2;
span的行数怎么算
举个具体的例子先:假设每一行的高度为20px,行距为10px,Item的高度是230px,则需要span的行数S =(230+10)/(10+20) = 8
为什么要额外加上行距10?这是因为我们需要留白空隙,所以实际需要的高度要加上行距,你也可以理解为这个行距加上盒子的高度才是一个Grid System里的一个单元,看下图:
现在考虑一般情况,假设行高为
R,行距为G,Item的高为H(注意Item的高需要包括它的Margin和padding等构成盒子的整个高度),则
S = (H+G) / (R+G)
行数出现分数怎么办
现实可能不能如你所愿,根据上面公式的计算,S可能是个分数,我们知道span属性后面只能接整数的值,这时候该怎么办呢。就算取相近的整数也大概率会出现下面这种情况,你可以看到间隙是大小不一的,很影响美观
总体的思路是我们只要想办法S是整数就好了(听君一席话......)
还是上面的例子:假设每一行的高度为20px,行距为10px,我们只要保证内容的高度+行距是30的整数即可,不过这个不太现实,因为现实可能是高度为任意整数的图片,甚至带分数XXX.xx * XXX.xx(这个前后端配合好还是可能的,就是需要接受的图片比较规范)
极端的方式:把Grid的每一行设置为1(注意:一行的实际高度等于行高加行距之和喔),任何整数除以1都能整除,出现时分数的情况可以先取整(问题解决)看下图
(有点吓人,但是也是个解决方法)
总结
这个方法实现比较简单,而且因为是用Grid实现的,非常的Responsive,而且新增的每一个图片Grid都会自动加到高度最短的那一列。不过目前我不知道性能怎么样,本地倒是不至于卡顿。本文是匆忙写的,有错误和疑惑请指出。还有这个只是个非常简单的模拟,实际的业务可能会涉及到更多的麻烦,比如加上文字标题该怎么处理,图片加载不出来该怎么办,窗口尺寸发生变化时,需要重新计算各个图片的高度等