2021, you still can’t waterfall layout? (three reliable JS schemes + n unreliable CSS schemes)

Time:2022-5-8

In the spirit of practicality, let’s share it todayWaterfall flow layoutYesterday, a little brother asked me how to do it. I looked for it for a long time and didn’t find it. It was written on the intranet)。Demo address: http://www.lilnong.top/static/html/waterfall.html

What is the waterfall layout?

for instancePetal netMushroom Street(I’ll map it below). These websites use waterfall layout when displaying content.

We also want to make a presentation of our design draft(Fixed width, variable height)Waterfall flow is a great solution.
The core of waterfall flow layout is based on a grid layout, andThe height of the list of items contained in each row is random(the height changes dynamically with their own content), and each item list is arranged in a stack. The most important thing is that there is no redundant spacing between stacks, and the difference is large. Let’s see what the waterfall layout looks like.

website Mushroom Street Petal net JD.COM VV
screenshot 2021, you still can't waterfall layout? (three reliable JS schemes + n unreliable CSS schemes) 2021, you still can't waterfall layout? (three reliable JS schemes + n unreliable CSS schemes) 2021, you still can't waterfall layout? (three reliable JS schemes + n unreliable CSS schemes) 2021, you still can't waterfall layout? (three reliable JS schemes + n unreliable CSS schemes)
programme Sub channel absolute

grid、inline、float Magic scheme

It can also be regarded as a pure CSS scheme. In essence, it isDependent document flow, from left to right, from top to bottom.

programme grid inline float bootstrap-grid
screenshot 2021, you still can't waterfall layout? (three reliable JS schemes + n unreliable CSS schemes) 2021, you still can't waterfall layout? (three reliable JS schemes + n unreliable CSS schemes) 2021, you still can't waterfall layout? (three reliable JS schemes + n unreliable CSS schemes) 2021, you still can't waterfall layout? (three reliable JS schemes + n unreliable CSS schemes)

You can see that there are very obvious differences in the document flow layoutthat ‘s okWhen a line is opened, it will leave blank, and lines will not overlap. hereThe most magical thing is the float layoutYes.

DOM structure

Div.list // set gird or block. Be careful to clear the float
  Div.item // set to inline or float to enable it to flow
    IMG // set fixed width, adaptive height, spacing, etc.

Grid scheme description

.wrap-waterfall--grid img{vertical-align: top;width: 100px}
.wrap-waterfall--grid .list{
    display: grid;
    grid-gap: 10px;
    /*It can be seen that the grid size and occupied position need to be set in advance*/
    grid-template-columns: repeat(4, 1fr);
    grid-auto-rows: minmax(50px, auto);
}

Grid is better than flex in some cases. For example, needBreak through the limit of row, but only forFixed layout, as shown in the following figure, how would you implement it without grid?

2021, you still can't waterfall layout? (three reliable JS schemes + n unreliable CSS schemes)

The scheme of realizing waterfall flow layout with gird in network transmissionBut I saw some of them. They are either color patches or image deformation and cutting. The scheme is to set the height with nth child,It’s terrible

CSS implementation of columns and flexUnreliable scheme

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 vertical first and then horizontal
  • (columns) compatibility issues
  • (Flex) if a fixed height is required, the set column will be exceeded and the set column cannot be filled.
programme columns flex
screenshot 2021, you still can't waterfall layout? (three reliable JS schemes + n unreliable CSS schemes) 2021, you still can't waterfall layout? (three reliable JS schemes + n unreliable CSS schemes)

Columns scheme

Natural support. You only need to set it for the parentcolumns: 4; column-gap: 6px;

Flex solution

flex-flow: column wrap;height: 2300px;By default, it is arranged horizontally. It is modified to be arranged vertically and allows line wrapping. Then, the content is wrapped by a fixed height.

Absolute, channel height calculation schemeReliable scheme

programme absolute Residual channel Calculate height sub channel
Screenshot of head 2021, you still can't waterfall layout? (three reliable JS schemes + n unreliable CSS schemes) 2021, you still can't waterfall layout? (three reliable JS schemes + n unreliable CSS schemes) 2021, you still can't waterfall layout? (three reliable JS schemes + n unreliable CSS schemes)

The plan here isIt’s reliable, which can meet our use requirements.

Let’s recall our requirements: show some content, and the content has characteristicsFixed width, variable height。 The variable height is generally caused by the inconsistent length or height of the content. The common content is divided into two typesText and pictures

  1. If there is no asynchronous font, it can be understood that the height of the box can be obtained by synchronization.
  2. In the case of pictures, because the loading is asynchronous, it is also asynchronous to obtain the real height of the box. But there are generally two situations here

    1. If there is no height, you can monitor the completion of image loading through onload. Wait until the picture is loaded to get the height.
    2. With height, this scheme is generally used in cover pictures or articles. When uploading pictures, the size of the original picture will be saved. At this time, we can directly use the existing data.

Get picture height

//Used to get the true height of the picture
naturalHeight: 1180
//Used to get the true width of the picture
naturalWidth: 1200

//The user obtains the current rendering height of the picture (which will be affected by CSS)
height: 98
//The user obtains the current rendering width of the picture (which will be affected by CSS)
width: 100

//Returns whether the browser has finished loading the image. If the loading is completed, it returns true; otherwise, it returns fasle.
Complete attribute
//You can monitor the action of picture loading
onload

Based on the above content, we can judge the complete attribute first,

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,
                })
            })
        })
    }
}
/*
//Test case
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 height calculation scheme

becauseOrdinary layout can no longer meet our needs, so we can consider passingposition: absoluteMakeThe content is displayed by absolute positioning

The core operation isMaintain the left and top of each elementThen use left and top to render to the correct position.

getListPosition(){
    //Viewport width / width of each column is divided into several columns
    let col = this.screenWidth / this.itemWidth >> 0;
    var arr = [];
    for(var i = 0; i < col; i++) arr.push({
        list: [],
        height: 0,
    })
    //Traverse all elements
    this.listInfo.forEach((item,idx)=>{
        //Find the lowest column
        var colIndex = 0;
        for(var i = 1; i < col; i++){
            if(arr[colIndex].height > arr[i].height){
                // colItem = arr[i]
                colIndex = i
            }
        }
        //Modify the information of the element
        //Belonging column
        item.line = colIndex;
        //Calculated top distance
        item.top = arr[colIndex].height+ 'px';
        //Left distance after calculation
        item.left = colIndex * (this.itemWidth + 10) + 'px'

        //Accumulation operation
        arr[colIndex].list.push(item);
        arr[colIndex].height += item.height + 10;
    })
    return arr
},

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

Render to different channels IDX% 4 according to the subscript

Because the last scheme used absolute positioning, is there a scheme without absolute positioning? Back to our pointFixed width, variable height, we can completely realize it by rendering separately and abandoning absolute.

jsGroupList(){
    return this.list.reduce((s,n,idx)=>{
        //Allocate the column directly according to the subscript
        s[idx % 4].push({idx: idx, item: n})
        return s
    }, [[],[],[],[],])
},

At the beginning, it realizes similar functions, but there is a disadvantage(Come to the comment area and reply)。

Calculate the height and divide the channels to avoid absolute

Because the previous scheme is classified by subscript, in fact, waterfall flow is classified by 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: []},
    ]
    //Traverse each element
    for(var i = 0; i < this.list.length; i++){
        //The operation is performed when the element has a size.
        if(!this.listInfo[i].height) return list;
        //By default, the first channel is the minimum height column
        var minHeightItem = list[0];
        //Calculate minimum height column
        list.forEach(v=>{
            if(v.height < minHeightItem.height) minHeightItem = v
        })
        //Add the new element height to the column.
        minHeightItem.height += this.listInfo[i].height
        //Push the new element into the column
        minHeightItem.list.push({idx: i, item: this.list[i]})
    }
    return list;
},

summary

Well, here are all the schemes I can think of. Do you have any plans? We can discuss the feasibility in the comment area. Next is the summary of our plan.

programme advantage shortcoming comment
columns Simple and pure CSS scheme compatibility
flex Fixed height is required, and filling is difficult to control
float、inline、bootstrapGrid There is no point. Most of them can’t use this scheme
grid It can be implemented through Nth child simulation or wait for compatibility masonry
absolute Good effect JS computing unlimited possibilities
JS common channel Filling is difficult to control
JS optimization channel Good effect, no absolute positioning It is not easy to control when there are operations such as column exaggeration

Postscript

  1. CSS grid waterfall flow layout under Firefox (grid template rows: Mason) I actually found, ha ha, an attribute that is almost unsupported.

Recommended Today

Microservice architecture * 3.5 source code analysis of Nacos service registration and discovery

catalogue preface 1. The client registers in the Nacos Registry (client perspective) 1.1 specifications and standards provided by spring cloud 1.2 automatic configuration class of Nacos 1.3 listen to the service initialization event abstractautoserviceregistration bind() 1.4 logic for registering service instances: nacosserviceregistry register() 1.4.1 heartbeat mechanism beatreactor addBeatInfo() 1.4.2 registration service namingproxy registerService() 1.5 send […]