支持 由左至右,由上至下的 horizontalOrder 方式 和 默认的按直行高度最小的方式布局。
    使用 Promise 异步下载图片,async 与 iterator 使依次布局。

    1. <div class="masonry"></div>
    .masonry {
      position: relative;
      width: 1200px;
      margin: 0 auto;
    }
    
    .item {
      position: absolute;
      counter-increment: item-counter;
    }
    
    .item img {
      display: block;
      width: 100%;
      height: auto;
    }
    
    .item::after {
      position: absolute;
      display: block;
      top: 2px;
      left: 2px;
      width: 24px;
      height: 24px;
      text-align: center;
      line-height: 24px;
      background-color: rgba(0, 0, 0, 0.5);
      color: #fff;
      content: counter(item-counter);
    }
    
    class Waterfall {
      constructor(opt, initData) {
        this.data = initData || [];
        this.el = document.querySelector(opt.el);
    
        this.column = opt.column;
        this.gap = opt.gap;
        this.horizontalOrder = opt.horizontalOrder || false;
    
        this.itemWidth = (this.el.offsetWidth - (this.column - 1) * this.gap) / this.column;
        this.heightArr = [];
    
        this.init();
        window.Waterfall = Waterfall;
      }
    
      init() {
        this.render();
      }
    
      async render() {
        const iter = this.data[Symbol.iterator]();
    
        let src = iter.next().value;
        let oDiv = null, index = 0;
        while (src) {
          oDiv = await this.createImgTag(src);
    
          if (this.horizontalOrder) {
            this.renderHorizontalOrder(oDiv, index);
          } else {
            this.renderLayout(oDiv, index);
          }
    
          src = iter.next().value;
          index++;
        }
      }
    
      renderLayout(item, index) {
        item.style.width = this.itemWidth + 'px';
        if (index < this.column) {
          item.style.top = '0px';
          item.style.left = index * (this.itemWidth + this.gap) + "px";
          this.el.appendChild(item);
          this.heightArr.push(item.offsetHeight);
        } else {
          const oItems = this.el.querySelectorAll('div');
          const minIdx = getMinIdx(this.heightArr);
          item.style.left = oItems[minIdx].offsetLeft + 'px';
          item.style.top = (this.heightArr[minIdx] + this.gap) + 'px';
    
          this.el.appendChild(item);
          this.heightArr[minIdx] += item.offsetHeight + this.gap;
        }
    
        function getMinIdx(arr) {
          return arr.indexOf(Math.min(...arr));
        }
      }
    
      renderHorizontalOrder(item, index) {
        item.style.width = this.itemWidth + 'px';
        if (index < this.column) {
          item.style.top = '0px';
          item.style.left = index * (this.itemWidth + this.gap) + "px";
          this.el.appendChild(item);
          this.heightArr.push(item.offsetHeight);
        } else {
          const idx = index % this.column;
          item.style.left = idx * (this.itemWidth + this.gap) + 'px';
          item.style.top = (this.heightArr[idx] + this.gap) + 'px';
    
          this.el.appendChild(item);
          this.heightArr[idx] += item.offsetHeight + this.gap;
        }
    
      }
    
      createImgTag(imgSrc) {
        const promise = new Promise((resolve, reject) => {
          const oDiv = document.createElement('div');
          oDiv.className = 'item';
          const oImg = document.createElement('img');
          oImg.onload = () => {
            oDiv.appendChild(oImg);
            resolve(oDiv);
          };
          oImg.onerror = () => {
            const error = new Error('图片加载失败 ${imgSrc}');
            reject(error)
          }
          oImg.src = imgSrc;
        })
        return promise;
      }
    
    }
    
    const myRandom = (min, max) => {
      return Math.floor(Math.random() * (max - min + 1) + min);
    }
    
    const wf = new Waterfall(
      {
        el: '.masonry',
        column: 5, // column-count
        gap: 3, // column-gap
      },
      new Array(50).fill(undefined).map((_, index) => {
        return `https://picsum.photos/${myRandom(200, 700)}/${myRandom(200, 700)}?random=${index + 1}`
      })
    )
    

    点击查看【codepen】
    点击查看【codepen】