实现瀑布流的两种方法

489 阅读2分钟

方法1: css

使用css 只需要在容器盒子里加上这两行代码,就可以实现瀑布流,不在过多赘述

    .content-box {
        // 列数
        columns: 5;
        // 间隙值
        column-gap: 10px;
    }

方法2: 使用js

分析代码逻辑:

  1. 首先将im的盒子设置绝对定位,其父元素设置相对定位.

html部分结构:

<div class="content-box">
    <div class="img-box"><img src="https://c-ssl.duitang.com/uploads/item/202009/15/20200915235152_FsBXU.thumb.1000_0.jpeg" class="img">
    </div>
    <div class="img-box"><img src="https://c-ssl.duitang.com/uploads/item/202004/01/20200401164316_i5rh2.thumb.1000_0.jpeg" class="img">
    </div>
    <-- ......-->
</div>   

css代码:

.content-box {
            width: 1200px;
            position: relative;
            margin: 50px auto;
            border: 1px solid rgba(253, 114, 109, 0.5);
            box-sizing: border-box;
        }
        
        .img-box .img {
            width: 100%;
            height: 100%;
        }
        
        .img-box {
            position: absolute;
            box-sizing: border-box;
        }

2.计算出每一列所需要的宽度:

Snipaste_2021-08-20_13-39-58.png

很明显, 每一行的宽度= (容器宽度-间隙值(列数-1))/列数*

  1. 渲染下一行第一个时,首先找到上一行中,盒子底部距离容器顶部距离最小的.然后将图片插入,并更新现存的列的高度,然后循环这一步

Snipaste_2021-08-20_13-39-58.png

4.因为使用的是网络图片而不是本地图片,所以要等待图片加载完成后(或图片预加载)再进行瀑布流布局,否则会获取不到图片真实高度

代码实现:

(function() {
            var Waterfall = function(opt) {
                this.el = document.getElementsByClassName(opt.el)[0]
                this.columns = opt.columns
                this.gap = opt.gap
                    // 每个盒子
                this.items = this.el.getElementsByTagName('div')
                    // 每个盒子的宽度
                this.imgWith = (this.el.offsetWidth - this.gap * (this.columns - 1)) / this.columns
                    // 存储每一列的当前高度
                this.heightArr = []
                this.init()
            }
            Waterfall.prototype.init = function() {
                this.render()
            }
            Waterfall.prototype.render = function() {
                var item = null,
                    minIndex = -1
                for (var i = 0; i < this.items.length; i++) {
                    item = this.items[i]
                        // 设置每个盒子的宽度
                    item.style.width = this.imgWith + 'px'
                        // 先确定第一行的位置
                    if (i < this.columns) {
                        item.style.top = '0px'
                        item.style.left = (this.gap + this.imgWith) * i + 'px'
                        this.heightArr[i] = item.offsetHeight
                    } else {
                        // 找到最小高度的列数
                        minIndex = getMinIndex(this.heightArr)

                        // 把图片加上去
                        item.style.top = this.heightArr[minIndex] + this.gap + 'px'
                            // left值与第一行相同列数的盒子相同
                        item.style.left = this.items[minIndex].offsetLeft + 'px'
                            // 更新数组 之前的值加上现在的图片高度,再加上gap
                        this.heightArr[minIndex] += item.offsetHeight + this.gap
                    }

                }
            }

            function getMinIndex(arr) {
                return arr.indexOf(Math.min.apply(null, arr))
            }
            window.Waterfall = Waterfall
        })()
        // 最后一个图片加载完成再开始排版
        var imgCount = document.getElementsByClassName('img')
        imgCount[imgCount.length - 1].onload = function() {
            new Waterfall({
                el: 'content-box',
                columns: 5,
                gap: 5
            })
        }
  1. 当然,你也可以按顺序依次插入,只需要更改render()里面的代码,并删除储存高度的数组:
 Waterfall.prototype.render = function() {
                var item = null,
                    index = 0

                for (var i = 0; i < this.items.length; i++) {
                    item = this.items[i]
                        // 设置每个盒子的宽度
                    item.style.width = this.imgWith + 'px'
                        // 先确定第一行的位置
                    if (i < this.columns) {
                        item.style.top = '0px'
                        item.style.left = (this.gap + this.imgWith) * i + 'px'
                        this.heightArr[i] = item.offsetHeight
                    } else {
                        // 当前正在第几列? i 只能在0-column中循环
                        index = index < this.columns ? index : -1
                        index++
                        // 把图片加上去
                        item.style.top = this.heightArr[index] + this.gap + 'px'
                            // left值与第一行相同列数的盒子相同
                        item.style.left = this.items[index].offsetLeft + 'px'
                            // 更新数组 之前的值加上现在的图片高度,再加上gap
                        this.heightArr[index] += item.offsetHeight + this.gap
                    }

                }
            }

虽然这会导致结尾图片不齐的问题:

Snipaste_2021-08-20_13-39-58.png

且部分图片仍然会重叠。