写一个jQuery瀑布流布局

这是我毕业闲着没事写着玩的,后面转移博客系统,处理过后也不知道跑步跑得起来。

这里需要注意的是,也是我当时忽略了的一个点:就是我们应该先拿到图片的宽高再进行渲染,不然第一次加载是会有问题的。

但是呢,图片如果很大的话,我们就会出现渲染阻塞的情况,所以我们需要在下载完成之前我们就应该拿到相关宽高数据。

你也可以到我的GitHub下载自己捣腾,点此下载

jQuery瀑布流

一、准备工作

要求:

1.布局要随着窗口大小的改变而自适应。

2.当页面滑动到底部时,自动加载。

原理:

在写这个之前我们要明白其原理,网上有很多关于瀑布流原理相关的文章,大致是三种,差不多都是引用淘宝UED的,但该文的原文链接已经挂了,在网上大家可以搜到大致的文章。我在这里也不多做赘述,简单的把我所用到的原理写下来。

1.子元素使用绝对定位

说明:之所以使用绝对定位,因为我们要随着屏幕的改变而改变子元素在屏幕中的位置。所以选择使用绝对定位,这也是线上最优的一种方法。具体图示:

wlxzo4wsE8wt9AB

图片中所说的元素出现的顺序与实际图片出现的顺序有所出入,下面我会做详细的说明。

2.计算好子元素宽度及margin

在写代码的过程中,我们要确定好子元素的宽度,可以使用代码定义,也可以写死,我写的这个例子就是写死了得。宽度固定式200px,margin定的10px。其中的图片定的是180px,margin同样定义的是10。之所以需要这个宽度,确定子元素在X轴上的位置就需要靠他来定位的。当然一行能摆放多少个也需要用到它。

3.计算好所有子元素的高度

这里计算的高度是用来确定子元素在Y轴上的定位,当然我们按照宽度定义的第一行top都为零。高度逐行递加保存。但需要注意的是第n+1行如何摆放是按照第n行最低高度开始逐级摆放的。这里第n行的高度是之前所有同一列子元素高度的和。

4.按照最低高度逐级摆放

5.判断滚动条位置

当滚动条到达底部时,我们向页面添加新的子元素。新的子元素依然要获取它的高度,并将它与页面初始化高度相加。

优缺点:

优点

  • 是最优的一种方案,方便添加数据内容;
  • 窗口变化,列数/数据块都会自动调整;

缺点

  • JS 动态计算数据块位置,当窗口缩放频繁,可能会狂耗性能;
  • 需要知道子元素的高度;

二、具体实现

首先文档结构

HTML

<div class="main-box">
    <div class="son-box"><img src="img/img_001.jpg" alt=""></div>
    <div class="son-box"><img src="img/img_002.jpg" alt=""></div>
    <div class="son-box"><img src="img/img_003.jpg" alt=""></div>
    <div class="son-box"><img src="img/img_004.jpg" alt=""></div>
    <div class="son-box"><img src="img/img_005.jpg" alt=""></div>
    <div class="son-box"><img src="img/img_006.jpg" alt=""></div>
    .....
</div>

CSS

body {
    background-color: aqua;
}

.main-box {
    position: relative;
    margin: 20px auto 0;
    min-width: 420px;
    max-width: 1200px;
    height: auto;
}

.main-box .son-box {
    position: absolute;
    box-sizing: border-box;
    display: inline-block;
    *display: block;
    *zoom: 1;
    width: 200px;
    background-color: #fff;
    -webkit-border-radius: 6px;
    -moz-border-radius: 6px;
    border-radius: 6px;
    -webkit-transition: all 0.7s ease-in 0.1s;
    -moz-transition: all 0.7s ease-in 0.1s;
    -o-transition: all 0.7s ease-out 0.1s;
    transition: all 0.7s ease-out 0.1s;
}

.main-box .son-box > img {
    width: 180px;
    height: auto;
    margin: 10px;
}

好了,具体的文档结构就是这个样子,你也可以根据自己的喜好来调整。那下面就具体的来解析一下JS

var $mainBox = $(".main-box");
var _sonBox = {};
//获取所有子元素 
_sonBox.sonAll = $(".son-box");

// /获取子元素宽度及间距 
_sonBox.sonW = _sonBox.sonAll[0].offsetWidth + 10;
//定义一个数组,用来保存页面初始化生成的高度 
_sonBox.Arr = [];

function CalAgg() {
    //主盒子布局 

    if (document.body.clientWidth < 1200) {
        //当页面宽度小于1200px时根据窗口大小决定一行子元素的数量
        _sonBox.sonAgg = parseInt(document.body.clientWidth / _sonBox.sonW);
        //计算出这一行实际占用多大的宽度并赋值给子元素的父元素,用于水平居中 

        $mainBox.width(_sonBox.sonW * _sonBox.sonAgg);
    }
    else {
        //如果窗口大于1200px的话,那么久固定一行显示5个 

        _sonBox.sonAgg = 5;
        $mainBox.width(_sonBox.sonW * _sonBox.sonAgg);
    }
};

function Waterfall() {
    //记录高度 
    var ArrHeight = [];
    //计算每行最大包含多少元素 
    CalAgg();
    for (var i = 0; i < _sonBox.sonAll.length; i++) {
        //循环到每个子元素,并获取它的高度,并保存 
        _sonBox.sonH = _sonBox.sonAll.eq(i).height();
        if (i < _sonBox.sonAgg) {
            //判断是否为第一行,因为第一行最多是_sonBox.sonAgg个。 
            // 将获取的子元素高度保存到数组 
            ArrHeight[i] = _sonBox.sonH;
            _sonBox.sonAll.eq(i).css("top", "0");
            //在这里的i不可能超过_sonBox.sonAgg,那么按照顺序乘以单个子元素的宽度就是第一行的定位。
            _sonBox.sonAll.eq(i).css("left", i * _sonBox.sonW + "px");
        }
        else {
            //获取上一层最小高度
            _sonBox.minH = Math.min.apply({}, ArrHeight);
            //获取最小高度保存在数组中的位置 

            _sonBox.minPos = jQuery.inArray(_sonBox.minH, ArrHeight);
            //重新赋值高度 
            ArrHeight[_sonBox.minPos] += _sonBox.sonH + 10;
            //该子元素的Y轴定位就是最小高度的下面 

            _sonBox.sonAll.eq(i).css("top", _sonBox.minH + 10 + "px");
            //该子元素的X轴定位就是数组中最小高度的index值乘以子元素的宽度。 

            _sonBox.sonAll.eq(i).css("left", _sonBox.minPos * _sonBox.sonW + "px");
        }
    }
    //在这里将这个数组复制给另外一个数组,在滚动加载中使用 
    _sonBox.Arr = ArrHeight.concat();
};
window.onscroll = function () {
    //获取滚动条到底部的距离 

    var _bot = $(document).height() - $(window).height();
    //判断滚动条是否到达底部 
    if ($(document).scrollTop() == _bot) {
        //CalAgg(); 
        // 每次增加10条数据 
        for (var i = 0; i < 10; i++) {
            //获取上一层最小高度 
            _sonBox.minH = Math.min.apply({}, _sonBox.Arr);
            //获取最小高度保存在数组中的位置 

            _sonBox.minPos = jQuery.inArray(_sonBox.minH, _sonBox.Arr);
            //添加样式及拼接字符串 

            var _sonStyle = "top:" + (_sonBox.minH + 10) + "px;" + "left:" + _sonBox.minPos * _sonBox.sonW + "px;",
                _sonHtml = '<div class="son-box" style=' + _sonStyle + '><img src=' + _imgObj.imgUrl[i].Url + ' alt=""></div>';
            //向父元素结尾处添加 
            $mainBox.append(_sonHtml);
            //重新赋值高度,在循环中使用eq(-1)是获取子元素集合最后一个元素的高度,即在上一步中添加的新子元素的高度 
            _sonBox.sonH = _sonBox.sonAll = $(".son-box").eq(-1).height();
            _sonBox.Arr[_sonBox.minPos] += _sonBox.sonH + 10;
        }
        //重新获取所有子元素 
        _sonBox.sonAll = $(".son-box");
    }
};
// 在页面加载完成中调用,必须使用onload,等图片加载完成才知道子元素高度是多少。 
window.onload = function () {
    Waterfall();
};
//当窗口改变时调用 
window.onresize = function () {
    Waterfall();
};

这里是写完后的效果:戳这里

那么这么一个瀑布流的基本效果也就完成了,当然写的有点坑爹,代码优化也不太好,还有些小BUG时常出没,但大致使用是没有问题的。

在后面我也会写成一个插件,当然这个东西不推荐生产开发,毕竟网上有许许多多优秀的瀑布流插件。这个东西只是给跟我一样的小伙伴们练练手,参考参考。