方案一 absolute + js 常用方案,但性能消耗大
1 2 3 4 5 6 import Waterfall from "./js/Waterfall" ;window .onload = new Waterfall ({ $el : document .querySelector (".img-wrapper" ), count : 4 , gap : 10 , });
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 export default class Waterfall { constructor (options ) { this .$el = null ; this .count = 4 ; this .gap = 10 ; Object .assign (this , options); this .width = 0 ; this .items = []; this .H = []; this .flag = null ; this .init (); } init ( ) { this .items = Array .from (this .$el .children ); this .reset (); this .render (); } reset ( ) { this .flag = document .createDocumentFragment (); this .width = this .$el .clientWidth / this .count ; this .H = new Array (this .count ).fill (0 ); this .$el .innerHTML = "" ; } render ( ) { const { width, items, flag, H, gap } = this ; items.forEach ((item ) => { item.style .width = width + "px" ; item.style .position = "absolute" ; let img = item.querySelector ("img" ); if (img.complete ) { let tag = H.indexOf (Math .min (...H)); item.style .left = tag * (width + gap) + "px" ; item.style .top = H[tag] + "px" ; H[tag] += (img.height * width) / img.width + gap; flag.appendChild (item); } else { img.addEventListener ("load" , () => { let tag = H.indexOf (Math .min (...H)); item.style .left = tag * (width + gap) + "px" ; item.style .top = H[tag] + "px" ; H[tag] += (img.height * width) / img.width + gap; flag.appendChild (item); this .$el .append (flag); }); } }); this .$el .append (flag); } }
初始化,计算出列宽来,将 H 作为列高存储器,4 列那么就是[0,0,0,0]。然后收集子元素后,清除父容器内容。 遍历其子元素,设置其都为绝对定位,设置其列宽。后监听其下的图片加载是否完毕。 如果加载成功,那么计算应该在的位置,瀑布流的常规原则是哪一列数值最小就在那一列上设置新图片。当然他的相对高度和间距也要计算出来,同时在 H 当前列上要把高度存起来。 每次图片加载完就更新虚拟节点到父容器中。
方案二 grid + js 子元素高度差过大的情况下不建议使用
1 2 3 4 5 6 7 display : grid;grid-auto-rows : 5px ;// 子元素自动宽度,两列,5px 间距 grid-template-columns : repeat (auto-fill, calc (50% - 5px ));// 防止布局错乱 align-items : start;justify-content : space-between;
1 2 3 4 5 6 const el = this .$refs .root .$el ;const rows = Math .ceil (el.clientHeight / 5 ) + 2 ;el.style .gridRowEnd = `span ${rows} ` ;
方案三 column 先排上下再排左右,多次加载数据的场景不建议使用
1 2 column-count : 4 ; //列数column-gap : 10px ; //列间距
方案四 grid 兼容性感人,不建议使用
1 grid-template-rows : masonry;