瀑布图墙小调研

1,601 阅读5分钟

如题,一道标准的纵向瀑布图墙<横向就vice versa啦>,大体分为三个阶段,需求度依次递增: 阶段一:布局呈现瀑布流。 阶段二:每个item顺序应保持一致,先从左至右,再从上至下。 阶段三:避免某列过长或过短。也就是多列状态下,能根据每列长度确定下一个item应处的列。

市面上解决方案,也分别对应了不同的需求。

方案一:column-count ✮

满足:阶段一 优点:利用了css的column-count属性,方便快捷。 缺点:布局顺序是先从上至下,再从左至右。如图:

image

这种效果类似于将所有元素三等分,分别人为塞到三个container里的感觉。用普通定位亦能够达到。 另,我觉得按MDN的例子,column-count更适用的场景是给文字排版。

Ref:HOW TO: Pure CSS masonry layouts

方案二:flex + flex-flow: column wrap ✮

满足:阶段一 优点:同样方便快捷,并且相信对大部分人而言,flex比column-count要更亲切些。 缺点:column是纵向排列,所以面临同样的顺序问题。 不过,加一点小小的诀窍就可以化腐朽为神奇☟。

方案三:flex + flex-flow: column wrap + order ✮✮✮✮

满足:阶段一、阶段二 原理:使用flex的order属性将纵向顺序打乱成迷惑肉眼的横向顺序。

order属性规定了弹性容器中的可伸缩项目在布局时的顺序。元素按照order属性的值的增序进行布局。拥有相同order属性值的元素按照它们在源代码中出现的顺序进行布局。

关键代码:

/*以三竖列为例*/
.item:nth-child(3n+1) { order: 1; }
.item:nth-child(3n+2) { order: 2; }
.item:nth-child(3n+3) { order: 3; }

image

因order的权重最高,所以item元素进行了重排。从上而下布局时,order高的元素都会被推入到之后进行渲染。 一个小问题:

image

如上图,本来元素3应该在第3行的,但是由于第二列还有很多空位,元素3就顺势插入了第二列结尾。问题就来了,该如何将每列的元素显性分开,不让它们顺势填充呢? 那么每列中间就应该存在一个“分隔列”,它的宽度为0,高度100%,起隔离作用。

/*使分隔列的高度撑满整个主轴,也就是高100%*/
 flex-basis: 100%;
 width: 0;

此时整个渲染队列应为:1, 4, 7, 10, <.break>, 2, 5, 8, 11, <.break>, 3, 6, 9, 12 渲染结果看起来类似:

image

不过当然,分隔列的DOM节点也需要人为插入了。

<div class="container">
  <div class="item"></div>
  <div class="item"></div>
  <div class="item"></div>
  <div class="item"></div>
  ...
  <!--人为插入的分隔列-->
  <span class="item break"></span>
  <span class="item break"></span>
  <span class="item break"></span>
</div>

总的说来,这个方案我认为是纯css布局中数一数二的方案了,不算轻松,但是胜在清晰,甚至可以真的在实际业务中用起来。因此夺得了四颗星!

Ref: CSS masonry with flexbox, :nth-child(), and order

方案四:grid + 动态grid-row-end ✮✮✮

满足:阶段一、阶段二 原理:grid布局的本质是网格块,那么不定高度的元素,就可以以竖跨不定数量的网格块实现(也就是动态设置grid-row-end)。当然,网格块的高度越小,细度越大,也越不浪费多出来的网格空间。

image

重点:在页面onLoad后,取到每个grid的高度以及每个item块里实际内容的高度,两相一除,再动态设置item块应竖跨多少个item. 优点:思路挺不错的!创新星给一颗~ 缺点:比较复杂,另外因为需要在页面刚渲染完成后再操作样式的变化,所以可以肉眼看见整个图墙被撑开的过程。

Ref: Masonry style layout with CSSGrid

方案五:绝对布局 ✮✮✮✮

满足:阶段一、阶段二、阶段三 感慨:这种方式不可战胜的好处是:绝对不会出错!想怎么定位怎么定位,每次push进新的item时,通过计算已渲染列表的高度,新item想放哪一列就放哪一列。 实际业务场景:

花瓣网: 绝对定位,直接更改top及left,每个item的高度自动撑开。

/*某个图墙item*/
position:absolute;
left:504px;
top:6223px;
opacity:1;

pinterest: 绝对定位,通过translate更改坐标。不过item高度是已知值,有做懒加载。在窗口resize时,会重新渲染。

/*某个图墙item*/
top:0px;
left:0px;
transform:translateX(260px) translateY(28459px);
width:260px; 
height:695px;

总结

总的来说,如果要我自己选的话~当实现场景偏ui时,比如官网的人物介绍阿这种item有限的瀑布图,用flex是不错的选择;而当实现场景偏业务时,比方无限下拉加载的瀑布图墙,为考虑兼容性和更大的可操纵性,我依然会选择死板却不会错的绝对定位 :P

另外,蘑菇街首页的伪瀑布图墙实现,直接将内容分成几大竖列来渲染,也是一种不显得很聪明但实际很有效的办法喔,科科。