11
头图

In the spirit of practicality, let’s share the waterfall flow layout ( yesterday, a little brother asked me how to do it, but I didn’t find it after looking for it for a long time, ah, it was originally written on the intranet ). demo address: http://www.lilnong.top/static/html/waterfall.html

What is the waterfall flow layout?

For example, petal mesh , map below), these sites use waterfall flow layout when displaying content.

We also want to make a page to show our design draft ( fixed width, variable height ), waterfall flow is a great solution.
The core of the waterfall flow layout is based on a grid layout, and the of the item list contained in each row of is random 160f6120c70df3 (the height changes dynamically with its content), and each item list is arranged in a stack. The most important thing , There is no extra space difference between the stacks. Let's take a look at the picture above to see what the waterfall flow layout we are talking about looks like.

website Petal Net JingdongVV
Screenshotsimage.pngimage.pngimage.pngimage.png
ProgramSub-channelabsolute

grid, inline, float magic scheme

It can be regarded as a pure CSS solution. In essence, depends on the document flow , from left to right and top to bottom.

Programgridinlinefloatbootstrap-grid
Screenshotsimage.pngimage.pngimage.pngimage.png

It can be seen that there is a very obvious line in the document flow layout. When a line is stretched, it will leave a blank, and the line and the line will not overlap. The most magical thing about .

DOM structure

div.list     // 设置 gird 或者 block,注意清除浮动
  div.item   // 设置为 inline 或者 float,使其能流动
    img      // 设置定宽,高度自适应,间距等。

grid scheme description

.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);
}

grid is better than flex in some situations. For example, needs to break through the row limit , but it only applies to fixed layout , as shown in the following layout, how would you achieve it without using grid?

image.png

has a gird plan to achieve waterfall flow layout , but I saw a few of them that are not color blocks, but image deformation and cropping. The plan is to use nth-child to set the height. too scary .

, flex CSS implement 160f6120c70ffb unreliable solution

It is also a pure CSS scheme. Compared with the above scheme, the scheme is acceptable, but there are still some problems.

  • The order is first vertical, then horizontal
  • (Columns) compatibility issues
  • (Flex) Need to give a fixed height, it will exceed the setting column, and it will not be able to fill the setting column.
Programcolumnsflex
Screenshotsimage.pngimage.png

columns scheme

columns: 4; column-gap: 6px; supported, you only need to set the parent to 060f6120c710db.

flex scheme

flex-flow: column wrap;height: 2300px; is arranged horizontally by default. By modifying it to vertical arrangement and allowing line wrapping, then the content will be line-wrapped by a fixed height.

absolute, channel height calculation program reliable program

ProgramabsoluteSurplus channelCalculate the height sub-channel
Head screenshotimage.pngimage.pngimage.png

The solution here is and , which can meet our requirements.

Let's recall our needs: to show some content, the content has the characteristics of fixed width, variable height . The variable height is generally caused by the inconsistency of content length or height. Common content is divided into two types text and image .

  1. For text, in the absence of asynchronous fonts, it can be understood that the height of the box can be obtained synchronously.
  2. In the case of pictures, because the loading is asynchronous, obtaining the true height of the box is also asynchronous. But there are generally two situations

    1. If there is no height, then you can monitor the completion of the image loading through onload. Wait for the picture to load before getting the height.
    2. There are heights. This kind of scheme is generally used in cover images or articles. The original image size will be saved when uploading the image. At this time, we can directly use the existing data.

Get image height

// 用于获取图片的真实高度
naturalHeight: 1180
// 用于获取图片的真实宽度
naturalWidth: 1200

//用户获取图片当前的渲染高度(会受 css 影响)
height: 98
//用户获取图片当前的渲染宽度(会受 css 影响)
width: 100

// 可返回浏览器是否已完成对图像的加载。如果加载完成,则返回 true,否则返回 fasle。
complete 属性
// 可以监听到图片加载完成的动作
onload 

Based on the above content, then we can first determine the complete attribute,

function getImageSize(img){
    if(img.complete){
        return Promise.resolve({
            naturalHeight: img.naturalHeight,
            naturalWidth: img.naturalWidth,
            height: img.height,
            width: img.width,
        })
    }else{
        return new Promise((resolve, reject)=>{
            img.addEventListener('load', ()=>{
                resolve({
                    naturalHeight: img.naturalHeight,
                    naturalWidth: img.naturalWidth,
                    height: img.height,
                    width: img.width,
                })
            })
        })
    }
}
/*
// 测试用例
el = document.createElement('img');
el.src = 'http://cors-www.lilnong.top/favicon.ico?'+Math.random()

getImageSize(el).then(console.log).catch(console.error)
setTimeout(()=>getImageSize(el).then(console.log).catch(console.error), 1000)
*/

absolute calculation height scheme

Because the can no longer meet our needs , we can consider using position: absolute to make through absolute positioning.

The core operation is maintain the left and top each element, and then use left and top to render to the correct position.

getListPosition(){
    // 视口宽度 / 每列宽度 得出划分为几列
    let col = this.screenWidth / this.itemWidth >> 0;
    var arr = [];
    for(var i = 0; i < col; i++) arr.push({
        list: [],
        height: 0,
    })
    // 遍历所有元素
    this.listInfo.forEach((item,idx)=>{
        // 找到最低的一列
        var colIndex = 0;
        for(var i = 1; i < col; i++){
            if(arr[colIndex].height > arr[i].height){
                // colItem = arr[i]
                colIndex = i
            }
        }
        // 修改元素的信息
        // 所属列
        item.line = colIndex;
        // 计算之后的 top 距离
        item.top = arr[colIndex].height+ 'px';
        // 计算之后的 left 距离
        item.left = colIndex * (this.itemWidth + 10) + 'px'

        // 累加操作
        arr[colIndex].list.push(item);
        arr[colIndex].height += item.height + 10;
    })
    return arr
},

Through calculation, we can find that the position of each element under the waterfall flow layout can be achieved through absolute positioning.

According to the subscript, to render to different channels idx% 4

Because the last solution used absolute positioning, is there any solution that does not require absolute positioning? back to our problem point, 160f6120c7134f fixed width, variable height , then we can completely abandon absolute by rendering separately.

jsGroupList(){
    return this.list.reduce((s,n,idx)=>{
        // 根据下标,直接分配所属列
        s[idx % 4].push({idx: idx, item: n})
        return s
    }, [[],[],[],[],])
},

Look at the beginning to achieve a similar function, but there is a drawback ( come to the comment area and reply ).

Calculate the height and then divide the channel to avoid absolute

Because the previous scheme is classified according to the standard, in fact, the waterfall is classified according to the height, so we change the classification condition to the lowest column.

jsGroupHeightList(){
    var list = [
        {height: 0, list: []},{height: 0, list: []},
        {height: 0, list: []},{height: 0, list: []},
    ]
    // 遍历每个元素
    for(var i = 0; i < this.list.length; i++){
        // 当元素有大小的时候在进行操作。
        if(!this.listInfo[i].height) return list;
        // 默认第一个通道是最小高度列
        var minHeightItem = list[0];
        // 计算最小高度列
        list.forEach(v=>{
            if(v.height < minHeightItem.height) minHeightItem = v
        })
        // 把新的元素高度累加到列中。
        minHeightItem.height += this.listInfo[i].height
        // 把新的元素push到列中
        minHeightItem.list.push({idx: i, item: this.list[i]})
    }
    return list;
},

to sum up

Well, here I have introduced all the solutions I can think of. Do you have any plans? We can discuss the feasibility in the comment section. The next step is a summary of our plan.

ProgramadvantageDisadvantageReviews
columnsSimple, pure CSS solutioncompatibility-
flex-Need to fix the height, filling is difficult to control and other issues-
float、inline、bootstrapGrid--It's not big 🧊 can't use this solution
grid--Can be implemented by nth-child simulation, or wait for compatibility masonry
absoluteGood effect-Infinite possibilities of JS calculation
js ordinary channel-Filling is difficult to control-
js optimized channelGood effect, no absolute positioningIt is not very well controlled when there are operations such as exaggerating columns-

postscript

  1. CSS Grid waterfall flow layout under firefox (grid-template-rows: masonry) I actually found it, haha, a property that is hardly supported.

linong
29.2k 声望9.5k 粉丝

Read-Search-Ask