封装函数
/**
 * 实现瀑布流布局
 */
function waterFull(parent, child) {
    // 1. 父盒子居中
    // 1.1 获取所有的盒子
    var allBox = $(parent).getElementsByClassName(child);
    // 1.2 获取子盒子的宽度
    var boxWidth = allBox[0].offsetWidth;
    // 1.3 获取屏幕的宽度
    var screenW = document.documentElement.clientWidth;
    // 1.4 求出列数
    var cols = parseInt(screenW / boxWidth);

    var xyMargin = 16;



    // 2. 子盒子的定位
    // 2.1 定义高度数组
    var heightArr = [], boxHeight = 0, minBoxHeight = 0, minBoxIndex = 0;
    // 2.2 遍历子盒子
    for (var i = 0; i < allBox.length; i++) {
        // 2.2.1 求出每一个子盒子的高度
        boxHeight = allBox[i].offsetHeight + xyMargin;
        // 2.2.2 取出第一行盒子的高度放入高度数组
        if (i < cols) { // 第一行
            heightArr.push(boxHeight);

            allBox[i].style.position = "absolute";
            allBox[i].style.left = i * (boxWidth + xyMargin)+ 'px';
            allBox[i].style.top =  xyMargin + 'px';
        } else { // 剩余行
            // 1. 取出最矮的盒子高度
            minBoxHeight = _.min(heightArr);
            // 2. 求出最矮盒子对应的索引
            minBoxIndex = getMinBoxIndex(heightArr, minBoxHeight);
            // 3. 子盒子定位
            allBox[i].style.position = "absolute";
            allBox[i].style.left = minBoxIndex * (boxWidth + xyMargin)+ 'px';
            allBox[i].style.top = minBoxHeight +  xyMargin + 'px';
            // 4. 更新数组中的高度
            heightArr[minBoxIndex] += boxHeight;
        }
    }

    // 5. 更新父盒子的高度
    var parentHeight = allBox[allBox.length - 1].offsetTop + allBox[allBox.length - 1].offsetHeight;
    $(parent).style.height = parentHeight;
}
/**
 * 获取数组中最矮盒子高度的索引
 * @param arr
 * @param val
 * @returns {number}
 */
function getMinBoxIndex(arr, val) {
    for(var i=0; i<arr.length; i++){
        if(arr[i] === val){
            return i;
        }
    }
}
function $(id) {
    return typeof id === "string" ? document.getElementById(id) : null;
}

/**
 * 判断是否具备加载图片的条件,如果满足,自动填充
 */
function checkWillLoadImage() {
    // 1. 获取最后一个盒子
    var allBox = document.getElementsByClassName("box");
    var lastBox = allBox[allBox.length - 1];

    // 2. 求出最后一个盒子自身高度的一半 + offsetTop
    var lastBoxDis = lastBox.offsetHeight * 0.5 + lastBox.offsetTop;

    // 3. 求出屏幕的高度
    var screenW = document.body.clientHeight || document.documentElement.clientHeight;

    // 4. 求出页面偏离浏览器的高度
    var scrollTop = scroll().top;

    return lastBoxDis <= screenW + scrollTop;
}



知识点

  1. 瀑布流大概思想
    ① 每一个图片盒子的宽度是固定的,根据当前页面宽度,求出一行能有几列,进而求出最外层div的宽度进行设置并居中.
    ② 子盒子定位思想
    Ⅰ. 第一行顺次排列,用一个数组存储他们的高度
    Ⅱ. 第二行的第一张接在第一行最矮的一张下面,并将二者的高度和存入数组
    Ⅲ.排好第一行后,将剩余所有图片按照Ⅱ进行排列
    ③ 便利所有数据,重复瀑布流
  2. 子绝父相并不是父盒子一定要是相对定位,只要不是static即可
  3. 当页面缩放的时候,重新进行瀑布流定位,并且将第一行的style定位清除掉
  4. 节流
    ① 当添加监听事件的时候,如果请求非常频繁。比如滚动,可以利用定时器进行节流。
    ② 设置一个定时器,每隔固定的事件进行进行一次操作。

运行效果

Javascript特效:瀑布流_数组
Javascript特效:瀑布流_i++_02

代码

index.html

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
    <link rel="stylesheet" href="./css/index.css">
</head>
<body>
<div id="main">
    <div class="box"><div class="pic"><img src="images/img02.jpg" alt=""></div></div>
    <div class="box"><div class="pic"><img src="images/img03.jpg" alt=""></div></div>
    <div class="box"><div class="pic"><img src="images/img04.jpg" alt=""></div></div>
    <div class="box"><div class="pic"><img src="images/img05.jpg" alt=""></div></div>
    <div class="box"><div class="pic"><img src="images/img06.jpg" alt=""></div></div>
    <div class="box"><div class="pic"><img src="images/img07.jpg" alt=""></div></div>
    <div class="box"><div class="pic"><img src="images/img08.jpg" alt=""></div></div>
    <div class="box"><div class="pic"><img src="images/img09.jpg" alt=""></div></div>
    <div class="box"><div class="pic"><img src="images/img10.jpg" alt=""></div></div>
    <div class="box"><div class="pic"><img src="images/img11.jpg" alt=""></div></div>
    <div class="box"><div class="pic"><img src="images/img12.jpg" alt=""></div></div>
    <div class="box"><div class="pic"><img src="images/img13.jpg" alt=""></div></div>
    <div class="box"><div class="pic"><img src="images/img14.jpg" alt=""></div></div>
    <div class="box"><div class="pic"><img src="images/img15.jpg" alt=""></div></div>
    <div class="box"><div class="pic"><img src="images/img16.jpg" alt=""></div></div>
    <div class="box"><div class="pic"><img src="images/img17.jpg" alt=""></div></div>
    <div class="box"><div class="pic"><img src="images/img18.jpg" alt=""></div></div>
    <div class="box"><div class="pic"><img src="images/img19.jpg" alt=""></div></div>
    <div class="box"><div class="pic"><img src="images/img20.jpg" alt=""></div></div>
    <div class="box"><div class="pic"><img src="images/img21.jpg" alt=""></div></div>
    <div class="box"><div class="pic"><img src="images/img22.jpg" alt=""></div></div>
    <div class="box"><div class="pic"><img src="images/img23.jpg" alt=""></div></div>
    <div class="box"><div class="pic"><img src="images/img24.jpg" alt=""></div></div>
    <div class="box"><div class="pic"><img src="images/img25.jpg" alt=""></div></div>
    <div class="box"><div class="pic"><img src="images/img26.jpg" alt=""></div></div>
    <div class="box"><div class="pic"><img src="images/img27.jpg" alt=""></div></div>
    <div class="box"><div class="pic"><img src="images/img28.jpg" alt=""></div></div>
    <div class="box"><div class="pic"><img src="images/img29.jpg" alt=""></div></div>
    <div class="box"><div class="pic"><img src="images/img30.jpg" alt=""></div></div>
    <div class="box"><div class="pic"><img src="images/img31.jpg" alt=""></div></div>
    <div class="box"><div class="pic"><img src="images/img32.jpg" alt=""></div></div>
    <div class="box"><div class="pic"><img src="images/img33.jpg" alt=""></div></div>
    <div class="box"><div class="pic"><img src="images/img34.jpg" alt=""></div></div>
    <div class="box"><div class="pic"><img src="images/img35.jpg" alt=""></div></div>
    <div class="box"><div class="pic"><img src="images/img36.jpg" alt=""></div></div>
    <div class="box"><div class="pic"><img src="images/img37.jpg" alt=""></div></div>
    <div class="box"><div class="pic"><img src="images/img38.jpg" alt=""></div></div>
    <div class="box"><div class="pic"><img src="images/img39.jpg" alt=""></div></div>
    <div class="box"><div class="pic"><img src="images/img40.jpg" alt=""></div></div>
</div>

<script src="js/UnderScore.js"></script>
<script src="./js/MyTools.js"></script>
<script src="js/index.js"></script>
</body>
</html>

index.css

*{
    margin: 0;
    padding: 0;
    border: none;
}

img{
    vertical-align: top;
}

html,body{
    width: 100%;
    height: 100%;
}

/*子绝父相*/
*{
    margin: 0;
    padding: 0;
    border: none;
}

img{
    vertical-align: top;
}

html,body{
    width: 100%;
    height: 100%;
}

/*子绝父相*/
#main{
    position: relative;
}

.box{
    float: left;
    padding: 15px 0 0 15px;
}

.pic{
    padding: 10px;
    border: 1px solid #cccccc;
    border-radius: 10px;
}
.box img{
    width: 165px;
}



index.js

window.addEventListener('load', function (ev) {
    var timer = null,timer1= null;
    // 1. 实现瀑布流布局
    waterFull('main', 'box');
    // 2. 加载数据
    window.addEventListener('scroll',function (ev1) {
        clearInterval(timer1);
        timer1 = setInterval(function () {
            if (checkWillLoadNewBox()){ // 结果为真
                // 2.1 假数据
                var dataArr = [
                    {"src":"img02.jpg"},
                    {"src":"img03.jpg"},
                    {"src":"img05.jpg"},
                    {"src":"img04.jpg"},
                    {"src":"img15.jpg"},
                    {"src":"img06.jpg"},
                    {"src":"img08.jpg"},
                    {"src":"img29.jpg"},
                    {"src":"img30.jpg"},
                    {"src":"img12.jpg"}
                ];
                // 2.2 遍历数据
                for (var i = 0; i < dataArr.length; i++) {
                    var newBox = document.createElement('div');
                    newBox.className = 'box';
                    myTool.$('main').appendChild(newBox);

                    var newPic = document.createElement('div');
                    newPic.className = 'pic';
                    newBox.appendChild(newPic);

                    var newImg = document.createElement('Img');
                    newImg.src = 'images/'+dataArr[i].src;
                    newPic.appendChild(newImg);

                }
                // 2.3 重新进行瀑布流布局
                waterFull('main', 'box');
            }
        },200);
    });
    // 3. 页面尺寸发生改变重新布局
    window.addEventListener('resize',function (ev1) {
        clearInterval(timer);
        timer = setInterval(function () {
            waterFull('main', 'box');
        }, 200);
    })
});

/**
 * 实现瀑布流布局
 * @param {string}parent 父盒子id
 * @param {string}child 子盒子class
 */
function waterFull(parent, child) {
    // 1. 父盒子居中
    // 1.1 获取标签
    var allBox = myTool.$(parent).getElementsByClassName(child);
    // 1.2 获取其中一个子盒子的宽度
    var boxWidth = allBox[0].offsetWidth;
    // 1.3 获取文档的宽度
    var screenW = document.documentElement.clientWidth || document.body.clientWidth;
    // 1.4 求出列数
    var cols = parseInt(screenW / boxWidth);
    // 1.5 让父盒子剧中
    myTool.$(parent).style.width = cols * boxWidth + 'px';
    myTool.$(parent).style.margin = '0 auto';

    // 2. 子盒子定位
    // 2.1 定义变量
    var heightArr = [], boxHeight = 0, minBoxHeight = 0, minBoxIndex = 0;
    // 2.2 遍历所有盒子
    for (var i = 0; i < allBox.length; i++) {
        boxHeight = allBox[i].offsetHeight;
        // 2.3 判断
        if (i < cols) { // 第一行
            heightArr.push(boxHeight);
            allBox[i].style='';
        } else { // 剩余行
            // 2.4 取出数组中最矮盒子的高度
            minBoxHeight = _.min(heightArr);
            // 2.5 取出最矮盒子在数组中的索引
            minBoxIndex = getMinBoxIndex(heightArr, minBoxHeight);
            // 2.6 剩余子盒子定位
            allBox[i].style.position = 'absolute';
            allBox[i].style.left = minBoxIndex * boxWidth + 'px';
            allBox[i].style.top = minBoxHeight + 'px';
            // 2.7 更新高度
            heightArr[minBoxIndex] += boxHeight;
        }
    }
}

function getMinBoxIndex(arr, val) {
    for (var i = 0; i < arr.length; i++) {
        if (arr[i] === val) {
            return i;
        }
    }
}

/**郇凯
 * 判断页面高度+页面划出高度 与 最后一张图片高度一半+距页面上方距离 的大小
 * 如果大于,图片出现
 * @returns {boolean} 返回为真为假
 */
function checkWillLoadNewBox(){
    // 1 获取最后的盒子
    var allBox = myTool.$('main').getElementsByClassName('box');
    var lastBox = allBox[allBox.length - 1];

    // 2 求最后盒子高度的一半
    var lastBoxDis = lastBox.offsetHeight * 0.5 + lastBox.offsetTop;

    // 3. 求出页面高度
    var screenH = document.documentElement.clientHeight || document.body.clientHeight;

    // 4. 求出页面滚出浏览器高度
    var scrollTopH = myTool.scroll().top;

    // 5. 返回结果
    return lastBoxDis <= screenH + scrollTopH;
}