Article reference: juejin.cn/post/701465…

Method 1: column implementation

  • Column-count: defines how many columns the screen is divided into
  • Column-gap: Defines the distance between columns

    body{
        margin: 0;
        list-style: none;
    }
    #box{
        margin: 40px;
        column-count: 5;
        column-gap: 20px;
    }
    #box > li > img{
        width: 100%;
        height: 100%;
    }

Copy the code

Advantages:

  • Implement a simple
  • Image auto-fill regardless of image loading status

Disadvantages:

  • Compatibility is poor
  • The arrangement is always up and down, left and right, out of control

Method 2: Flex layout implementation

body{
    margin: 0;
    list-style: none;
}
#box{
    display: flex;
    flex-flow: column wrap;
    height: 2000px;
}
#box > li{
    margin: 10px;
    width: calc(100% / 4 - 20px);
}
#box > li > img{
    width: 100%;
    height: 100%;
}
Copy the code

Advantages:

  • Relatively simple implementation
  • Image auto-fill regardless of image loading status
  • The order can be changed to some extent

Disadvantages:

  • The height is fixed

You can use the above methods in small projects, but the actual development is still done in JS

Method three: JS implementation

export default class Waterfall {
    /* * $el: parent container * width: width of each image * items: all child elements * H: store height of each column * flag: virtual DOM node set * */
    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()
        console.log('ok')}init() {
        this.items = Array.from(this.$el.children)
        this.reset()
        this.render()
    }

    reset() {
        this.flag = document.createDocumentFragment()  // Create a blank document
        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')
            /* The image is loaded */
            if (img.complete) {
                /* Get the minimum height of each column */
                let tag = H.indexOf(Math.min(... H)); item.style.left = tag * (width + gap) +'px'
                item.style.top = H[tag] + 'px'
                img.style.width = '100%'
                img.style.height = '100%'
                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'
                    img.style.width = '100%'
                    img.style.height = '100%'
                    H[tag] += img.height * width / img.width + gap
                    flag.appendChild(item)
                    this.$el.append(flag)
                })
            }
        })
        this.$el.append(flag)
    }
};
Copy the code

Implementation principle:

  • Initialize, calculate the column width, using H as the column height memory. The child elements are then evaluated and the contents of the parent container are cleared.
  • Iterate over the child elements, set them to absolute positioning, set their column width, and then listen to see if the image under them has loaded.
  • If it’s loaded, then it calculates where it should be, and the core of the waterfall stream is which column has the smallest height and it sets up a new image on that column. Of course, its relative height and spacing are also calculated, and the height is stored on the current H.
  • Update the virtual node to the parent container every time the image is loaded.

Advantages:

  1. Flexible control, arbitrary expansion
  2. It can also be loaded indefinitely without worrying too much about compatibility
  3. Many animations can also be added to enhance the user experience

Disadvantages:

  1. Relatively complex implementation
  2. Image filling needs to consider the image loading state
  3. Performance is worse than pure CSS implementations