什么是瀑布流?

Pinterest 最早实现了瀑布流的网站,因此,瀑布流的英文名称是Pinterest-style layout

瀑布流特征

  • 每行的列数是固定的

  • 瀑布流中的元素等比例进行缩放

  • 元素宽度固定

  • 元素高度不固定

  • 第一行顶部对齐

  • 其他行通过遍历,找到上一行中,高度最小的列,放在它后面

向下滚动的时候,像瀑布往下流一样,因此得名。

使用 JavaScript 实现瀑布流的核心思想

不管什么效果,最终都要实现为 HTML/CSS 再进行渲染的。动手之前,要先考虑几个问题:

  • 可以设置元素的宽和高
  • 一行内可以有多个元素
  • 可以设置元素的位置

使用 relative + absolute 进行布局正合适,这几个特性都满足。

代码解析

CSS:

.masonry { 
  width: 100%;
  margin-top: 50px;
  position:relative;
}

.item { 
  z-index: 10;
  transition: 0.25s;
  overflow: hidden;
  position: absolute;
}

.item img{
  width: 100%;
  height:100%;
}

JS 代码:

(代码来自下方链接的博客,注释已经写的很完整)

        var pageWidth = $('.masonry').width();
        var columns = 4; // 4 列
        var itemWidth = parseInt(pageWidth/columns); // 得到item的宽度
        $(".article-item").width(itemWidth); // 设置到item的宽度

        var arr = [];

        $(".masonry .item").each(function(i){
            var height = $(this).find("img").height();
            var width = $(this).find("img").width();
            var bi = itemWidth/width; // 获取缩小的比值
            var boxheight = parseInt(height*bi); // 图片的高度*比值 = item的高度

            if (i < columns) {
                // 2- 确定第一行
                $(this).css({
                    top:0,
                    left:(itemWidth) * i
                });
                arr.push(boxheight);

            } else {
                // 其他行
                // 3- 找到数组中最小高度  和 它的索引
                console.log(arr.length, arr);
                var minHeight = arr[0];
                var index = 0;
                for (var j = 0; j < arr.length; j++) {
                    if (minHeight > arr[j]) {
                        minHeight = arr[j];
                        index = j;
                    }
                }
                // 4- 设置下一行的第一个盒子位置
                // top值就是最小列的高度
                $(this).css({
                    top:arr[index],
                    left:$(".masonry .article-item").eq(index).css("left")
                });

                // 5- 修改最小列的高度
                // 最小列的高度 = 当前自己的高度 + 拼接过来的高度
                arr[index] = arr[index] + boxheight;
            }
        });

其他

窗口大小发生变化时,重新计算窗口大小

function resetSize() {
        $(".masonry .item").each(function (i) {
            $(this).css({
                top: 'auto',
                left: 'auto',
                width: 'auto',
                height: 'auto',
            });
        })
    }

    window.onresize = function() {
        resetSize();
        waterFall();
    };

这里增加了个 resetSize 方法,在窗口发生变化时,首先重置元素的大小,可以避免高度计算不正确的Bug

增加行间距

大部分的时候,是不希望元素密密麻麻挤在一起的,需要增加一个边距。我是通过 CSS 对元素进行操作,来设置的:

.article-item {
    padding-right: 15px;
    padding-bottom: 15px;
    box-sizing: border-box;
}

最小高度计算

因为是 absolute 布局,所以默认的盒模型高度不适用,如果想给父元素设置高度,可以通过 js 进行计算:

        var maxItemOffsetTop = 0
        $(".masonry .item").each(function (it) {
            var offsetTop = $(this).offset().top
            if (offsetTop > maxItemOffsetTop) {
                maxItemOffsetTop = offsetTop
            }
        });


        var masonryOffsetTop = $(".masonry").offset().top
        var masonryMinHeight = maxItemOffsetTop + $(".masonry .article-item").last().height() - masonryOffsetTop

        $(".masonry").css('minHeight', masonryMinHeight)

这段代码是放在计算瀑布流的后面执行,以最后一行最高的元素为参考,进行的计算。


只要掌握了思想和计算方法,实现瀑布流还是很简单的。其他平台上的瀑布流实现的都差不多。下一篇讲一讲纯 CSS 来实现瀑布流。

###参考

https://ainyi.com/60



blog comments powered by Disqus

Published

2019-11-17

Categories


Tags