瀑布流效果

html结构

(Emmet)

(div.box>div.pic>img[src="images/$.jpg"])*23

           

![](images/1.jpg)

           

![](images/2.jpg)

           

![](images/3.jpg)

           

![](images/4.jpg)

css结构

*{

    margin:0;

    padding:0;

}

#main{

    position: relative;

}

.box{

    padding:10px 0 0 15px;

    /*display: inline-block;一行显示*/

    float: left;

}

.pic{

    padding: 10px;

    border:1px solid #ccc;

    border-radius: 5px;

    box-shadow: 0 0 5px #ccc;

}

.pic img{

    /*瀑布流特点*/

    width: 165px;

    height: auto;

}

一、JavaScript原生方法实现瀑布流布局

整个功能封装在waterfall()函数中。

图片定位

js文件是在head中引入,所以执行的脚本需要放在window.onload事件中。

因为我们进行操作的是main下的box元素,首先要先进行获取元素。

然鹅,js中没有提供专门获取Class名的元素的方法。又为了方面后面的函数的调用,我们将获取父元素main下的所有class为box的子元素的这个功能进行封装getByClass()。

window.onload = function () {

      waterfall('main','box');

}

function waterfall(parent,box){

    //把父元素main下所有class名为box的子元素都取出来

    var oParent = document.getElementById(parent);

    getByClass(oParent,box);

}

getByClass()实现的思路:若要获取父元素下所有class为特定名的子元素,首先要把父元素下所有子元素全部都取出来,然后进行遍历,判断每个子元素上的className是否和你传入的class名相等;如果相等,这个子元素就是我们要找的,然后把这些我们要找的子元素存储起来boxArr。

这里,通过Class取到的所有元素,最后的结果是数组类型(不止一个)。

获取到的元素要存储起来,boxArr.push()向数组的末尾添加元素。

//根据class获取元素

function getByClass(parent,clsName){

    var boxArr = new Array(),//用来存储获取到的所有class为box的元素

        oElements = parent.getElementsByTagName('*');//取出父元素下的所有子元素

    for (var i = 0; i < oElements.length; i++) {

        if(oElements[i].className == clsName){

            boxArr.push(oElements[i]);

        }

    }

    return boxArr;

}

getByClass()函数最后返回的是一个数组,var oBoxs = getByClass(oParent,box);声明一个变量oBoxs来接收获取到的所有元素。console.log(oBoxs.length);// 23 说明已经全部取出

这样就把ID为main的父元素下所有class为box的子元素都取出来了。

问题:

oElements[i].className == className  ,用这种方法判断,欠妥当。

因为现实项目中,className 不止一个,这样就永远没法相等,应该这样判断

oElements[i].className.indexOf(className) > 0

--

功能点

浏览器窗口大小变化时,页面中一行里图片的个数是固定的。即图片列数不随浏览器窗口大小的变化而变化。

思路:图片列数是固定值→使大盒子main的宽度值固定即可→图片的列数×一个box的宽

3个步骤:

确定列数:以当前的页面宽度,除以一个 box 的宽度,结果取整{Math.floor()}

确定 main 容器的宽度:列数(即每行中能容纳box的个数)乘以一个 box 的宽(也可以这样写:oParent.style.width = oBoxW*cols+'px';)

定位第一行盒子:将 box 集合作为数组取出,遍历子元素,加入入数组

一个box的宽=图片宽度165+内边距10×2+边框1×2 +填充15

在waterfall()中执行以下代码,设置main的宽度以及对齐方式。使用cssText属性以字符串的形式对其设置。offsetWidth计算的是没有外边距的盒子宽。

//计算整个页面显示的列数(页面宽/box的宽)

    var oBoxW = oBoxs[0].offsetWidth;//等宽

    // console.log(oBoxW);

    var cols = Math.floor(document.documentElement.clientWidth / oBoxW)//取整;

    // console.log(cols);

    //设置main的宽

    oParent.style.cssText = 'width:'+oBoxW*cols+'px;margin:0 auto;'

图片排序(盒子排列)

3个步骤:

找到上一行里高度最小的盒子(即空隙最大的地方)

把要排列队列里的第一个的盒子定位到这个空白处

(需要两个数值,第一个是上一行最矮盒子的高度【方法:Math.min.apply()】,第二个是上一行最矮盒子的左边距【两种办法:盒子宽最矮盒子下标;数组里最小盒子的offsetLeft。】) *

更新这一列的高度,最矮元素的高,加上当前盒子的高度

--

arrayObject.push() 方法可向数组的末尾添加一个或多个元素,并返回新的长度。

Math.min()返回的是一组数据中的最小值。

Math.min.apply(null,hArr)取数组中的最小值;apply()接收两个参数,一个是函数运行的作用域(this),第一个参数是null的情况下,this指向window,另一个是参数数组。

--

求最矮盒子的下标这里写出getMinhIndex(hArr,minH)函数,或者使用hArr.indexOf(minH)直接返回下标值。

getMinhIndex(hArr,minH)这里一旦找到这个最小值,就返回了索引号;若有多个最小高度值相等,那么返回的是这个队列中第一个出现的最小高度值。

//遍历数组中的每一个值,若与传入的特定值相等,返回该下标值。

function getMinhIndex(arr,val){

        for(var i in arr){

            if(arr[i]==val){

                return i;

            }

        }

    }

这样就找到了上一行最矮的那个盒子以及该盒子所在的索引号。接下来就对下一行第一个盒子进行绝对定位。这时候发现剩下的所有盒子重叠了。这是因为上一行所有图片的高度hArr[],是固定的那几个值,所以求得的最小值minH是固定的。即后面的所有盒子都堆在了这个固定最矮图片的下面。

解决:修改数组hArr[]里的高度值→改变最小高度值hArr[Index] += oBoxs[i].offsetHeight。最矮元素的高,加上当前盒子的高度,更新这一列的高度。

//waterfall()函数中执行。

//先把上一行图片的高度全都取出来,然后进行存储。再找出图片高度的最小值以及该图片所在的索引对下一个盒子进行绝对定位。

var hArr = [];//存放每一列高度的数组

    for (var i = 0; i < oBoxs.length; i++) {

        if (i

            //或hArr[i]=oBoxs[i].offsetHeight;

hArr.push(oBoxs[i].offsetHeight);    //获取第一行盒子的高度并进行存放

}else{

//下一行里第一张图片的定位

var minH = Math.min.apply(null,hArr);//获取最小值// console.log(minH);

var Index = getMinhIndex(hArr,minH);//获取索引值

//var Index = hArr.indexOf(minH);

oBoxs[i].style.position = 'absolute';

oBoxs[i].style.top = minH + 'px';//把图片加上一行中最矮的图片的底下

oBoxs[i].style.left = oBoxs[Index].offsetLeft + 'px';//px

//修改hArr中的最小值

hArr[Index] += oBoxs[i].offsetHeight;

        }

        console.log(hArr);

    }

图片加载功能

思路:

何时加载:滚动条x向上滚动的距离(scrollTop)与可视区页面高度(clientHeight)之和 大于最后一张图片的距离父元素顶端位置(offsetTop)与盒子高度(offsetHeight )的一半之和。

offsetTop+offsetHeight / 2是固定值,滚动条向下滚动的距离和页面向上偏离的距离相等,知道滚到当前队列中最后一张图片自身高度的一半或者该图片刚显露出来(自定义)开始加载其他的图片。滚动条向下滚动的距离和页面的可视区高度若小于这个固定值,说明没有滚到需要加载图片的时候。

怎么加载:json数据交换格式;创造元素并进行嵌套(appendChild()方法 语法:parent.appendChild(children))将数据信息渲染到页面中。

由于数据都是从后台来,这里模拟json格式的数据;首先进行遍历,然后创建盒子,塞到main盒子里。遍历给出的数据,将图片添加到数据块中渲染出来

首先实现何时加载功能checkScrollSlide().找出当前队列中的最后一个盒子oBoxs[oBoxs.length - 1],计算数值时一般计算机能接受的最小单位是像素,即整数,所以求盒子自身高度值的一半用到Math.floor()。

返回的是布尔型。是否加载。

//监测是否具备滚动加载数据块的条件

function checkScrollSlide(){

        var oParent = document.getElementById('main');

        var oBoxs = getByClass(oParent,'box');

        var lastBoxH = oBoxs[oBoxs.length - 1].offsetTop + Math.floor(oBoxs[oBoxs.length - 1].offsetHeight / 2);

        // console.log(lastBoxH);最后一个盒子到页面顶部的距离+自身高度的一半

        var scrollTop = document.body.scrollTop || document.documentElement.scrollTop;//滚动条向上滚动的距离

        // console.log(scrollTop);

        var height = document.body.clientHeight || document.documentElement.clientHeight;//页面可视区高度

        // console.log(height);

        return (scrollTop+height>lastBoxH)?true:false;//三元操作符

        // return scrollTop+height>lastBoxH;

    }

用到window.onscroll()滚动条滚动事件,在页面加载完毕后即window.onload()里触发。

dataInt是一个对象,以数组的形式存放数据信息。data为数据属性。dataInt.data.length数据的个数。

数据加载进来后,只是这些要渲染的数据只是被加到页面中,并没有进行图片定位和图片排序。会出现重叠和图片间有空白等现象。这时,需要再调用一下waterfall()函数。

这样,页面中滚动条不断下拉时,dataInt对象里几张图片就会不断的进行加载。

var dataInt = {"data":[{"src":'0.jpg'},{"src":'1.jpg'},{"src":'2.jpg'},{"src":'3.jpg'}]};//模拟后台数据

window.onscroll = function () {

        if (checkScrollSlide) {//为真

            //将数据块渲染到页面的尾部

            var oParent = document.getElementById('main');

            //遍历数据块

            for(var i=0;i

                //创建存放数据块的盒子并渲染到页面中

                var oBox = document.createElement('div');

                oBox.className = 'box';

                oParent.appendChild(oBox);

                var oPic = document.createElement('div');

                oPic.className ='pic';

                oBox.appendChild(oPic);

                var oImg = document.createElement('img');

                //获取数据块中的文件名

                oImg.src="images/"+dataInt.data[i].src;//都存放在固定文件images里

                oPic.appendChild(oImg);

            }

            waterfall('main','box');

        }

}

问题:

checkScrollSlide调用的问题

函数只要是要调用它进行执行的,都必须加括号。此时,函数()实际上等于函数的返回值。当然,有些没有返回值,但已经执行了函数体内的行为,这个是根本,就是说,只要加括号的,就代表将会执行函数体代码。

不加括号的,都是把函数名称作为函数的指针,用于传参,此时不是得到函数的结果,因为不会运行函数体代码。它只是传递了函数体所在的地址位置,在需要的时候好找到函数体去执行。

二、JQuery实现瀑布流布局

$(window).on('load',function(){

    waterfall();

    var dataInt = {"data":[{"src":'0.jpg'},{"src":'1.jpg'},{"src":'2.jpg'},{"src":'3.jpg'}]};

    $(window).on('scroll',function(){

        if(checkScrollSlide){

            //创建盒子并添加到页面中

            $.each(dataInt.data,function(key,value){

                var oBox=$('

').addClass('box').appendTo($('#main'));

                var oPic=$('

').addClass('pic').appendTo($(oBox));

// console.log(value);value是dataInt里的对象,即原生js对象,需加$装换成JQuery对象才能使用JQuery方法

var oImg=$('').attr('src','images/'+$(value).attr('src')).appendTo($(oPic));

})

waterfall();

}

})

});

function waterfall(){

    var $boxs=$('#main>div');//获取main下的一级div元素

    var w = $boxs.eq(0).outerWidth();//一个盒子的宽度包括填充和边框

    var cols = Math.floor($(window).width()/w);

    $('#main').width(w*cols).css('margin','0 auto');//设置main的宽度以及对齐方式

    var hArr = [];

  //数组遍历

    $boxs.each(function(index,value){

        // console.log(index);

        // console.log(value);DOM对象

        var h =$boxs.eq(index).outerHeight();

        if(index

            hArr[index]=h;

        }else{

            var minH = Math.min.apply(null,hArr);//最小值

            var minHIndex = $.inArray(minH,hArr);//索引

            //console.log(value);value是DOM对象,需加$装换成JQuery对象才能使用JQuery方法

            $(value).css({

                'position':'absolute',

                'top':minH+'px',

                'left':minHIndex*w+'px'

            })

            hArr[minHIndex]+=$boxs.eq(index).outerHeight();//更新数组

        }

    })

    // console.log(hArr);

}

function checkScrollSlide(){

    var $lastBox=$('#main>div').last();//获取最后一个元素

    var lastBoxDis=$lastBox.offset().top+Math.floor($lastBox.outerHeight()/2);

    var scrollTop=$(window).scrollTop();//滚动条滚动的距离

    var documentH=$(window).height();//页面可视区高度

    return (lastBoxDis

}

三、CSS3实现瀑布流布局

根据盒子的宽度设置column-width属性。这里一个box的宽=图片宽度165+内边距10×2+边框1×2 +填充15

这种方式不需要计算,只需要设置列宽,浏览器自动计算,性能高。但是列宽会随着浏览器窗口的大小进行改变,用户体验不好;另外图片排序是按照垂直顺序排列的。最后图片的加载需要JavaScript实现。

#main{

    /*position: relative;*/

    -webkit-column-width: 202px;

    -moz-column-width: 202px;

    -o-column-width: 202px;

    column-width: 202px;

}

       

       

       

       

推荐阅读更多精彩内容