javascript实现瀑布流布局实践思路教程

淘宝有部分页面中使用了瀑布流布局,淘宝的kissy框架加入了这种布局的插件,详细可以查看相关的API函数,在淘宝UED的博客中乔花写了有关瀑布流布局的文章。下面是用JQ框架实现瀑布流布局的实现思路。

1、设置一些全局变量,通过全局变量然后初始化页面,保存临时数据在变量中。初始化一些数据,计算宽度等值。

function water(opts) {
	this.container=opts.container;//容器
	this.context=opts.context;
	this.elem=opts.elem;//元素
	this.len=opts.len;//需要调用数据次数
	this.url=opts.url;//ajax地址
	this.colArr=[];//top数组 即列高
	this.containerW=960;//容器宽度
	this.colNum=4;//列数
	this.columnW=210; //元素宽度
	this.columnP=20; //元素padding
	this.columnM=13;  //元素margin
	this.columnOw=0; //元素的outerWidth
	this.i=0;//ajax判断需要
	this.b=false;//ajax判断需要
	this.threshold=10;//临界点
}
2、计算left ,top值,这个是最关键的,很多人不知道这个值是如何计算的。通过上面的代码,我们发现一个this.colArr这个数组,其实这个数组就是保存top值。首先我们初始化top值

if(_this.colArr.length==0) {
 //初始化,如果数组中未保存任何值,责初始化设置0
	for(var i=0,len=_this.colNum;i<len;i++) {
		_this.colArr[i]=0;
	}
}
根据一开始设置的列数,循环变量,给colArr数组设置初始值。top值如何计算呢?我们只要对元素进行遍历,第一行的初始值则都为0,故top=0, 每一个元素赋值以后,则对这个元素进行计算 _flow=元素高度+margin,然后重写colArr数组里面的数据。这里又有一个疑问,重写第几个。 考虑到瀑布流这种布局的方式,首先考虑的是填充最小高度的那个,即重写colArr中最小的值。left值的判断比较简单,可以获取数组中最小值的 index(由于列数和数组长度相同),然后乘以元素的宽度即可。

于是有下面的代码:

Array.max= function(array) {//获取数组中最大值
	return Math.max.apply(Math,array);
}
Array.min= function(array) {//获取数组中最小值
	return Math.min.apply(Math,array);
}
water.prototype= {
	flow: function(elem) {
	var _this=this;
		elem.find(_this.elem).each( function(i){
		/*遍历元素,将每个元素的高度+margin值(即为top值)保存在colArr数组中。一共四列,数组中每个值进行重新赋值,取数组中最小值,进行排列,将每个列一次先填满。*/
			var _elem=$(this);
			var _count=$.inArray(Array.min(_this.colArr),_this.colArr);//获取最小值的index
			var _flow=_this.colArr[_count];//通过最小值的index,从数组中获取值
			_elem.css({
				"top":_flow,
				"left":_count*_this.columnOw
			});//赋值 left为 _count(即列数)*元素宽度
			_this.sArr[_count]=_flow+_this.columnM;
			_this.colArr[_count]=_flow+_elem.outerHeight()+_this.columnM;//重新对数组赋值
		});
		var _max=Array.max(_this.colArr);
		_this.container.height(_max);
	}
}
有了上面的思路,接下来的填充和ajax效果就不多说,直接看代码比较好,对于scroll的控制,简单的讲下,其实和lazy-load相似,判断临界点和可视区域的距离,这里的临界点其实就是列的最小高度,即colArr里面最小值。

所有代码:

/*
 *autor:demon
 *time:2012.2.9
 * web:http://www.cssdemon.me
 * 瀑布流布局
 */
function water(opts) {
    this.container=opts.container;//容器
    this.context=opts.context;
    this.elem=opts.elem;//元素
    this.len=opts.len;//需要调用数据次数
    this.url=opts.url;//ajax地址
    this.colArr=[];//top数组 即列高
    this.containerW=960;//容器宽度
    this.colNum=4;//列数
    this.columnW=210; //元素宽度
    this.columnP=20; //元素padding
    this.columnM=13;  //元素margin
    this.columnOw=0; //元素的outerWidth
    this.i=0;//ajax判断需要
    this.b=false;//ajax判断需要
    this.threshold=10;//临界点
}
 
Array.max= function(array) {//获取数组中最大值
    return Math.max.apply(Math,array);
}
Array.min= function(array) {//获取数组中最小值
    return Math.min.apply(Math,array);
}
water.prototype= {
    init: function() {
        var _this=this;
        $("<div class='sCon'></div>").appendTo(_this.container);
        _this.columnOw=_this.columnW+_this.columnP+_this.columnM;//计算元素的宽度
        if(_this.colArr.length==0) {//初始化,如果数组中未保存任何值,责初始化设置0
            for(var i=0,len=_this.colNum;i<len;i++) {
                _this.colArr[i]=0;
            }
        }
        _this.flow(_this.context);
        _this.scoll(".sCon");
    },
    flow: function(elem) {
        var _this=this;
        elem.find(_this.elem).each( function(i) {
            /*
             遍历元素,将每个元素的高度+margin值(即为top值)
             保存在colArr数组中。一共四列,数组中每个值进行重新赋值,取数组中最小值,进行排列,将每个列一次先填满。
             */
            var _elem=$(this);
            var _count=$.inArray(Array.min(_this.colArr),_this.colArr);//获取最小值的index
            var _flow=_this.colArr[_count];//通过最小值的index,从数组中获取值
            _elem.css({
                "top":_flow,
                "left":_count*_this.columnOw
            });//赋值 left为 _count(即列数)*元素宽度
            //_this.sArr[_count]=_flow+_this.columnM;
            _this.colArr[_count]=_flow+_elem.outerHeight()+_this.columnM;//重新对数组赋值
        });
        var _max=Array.max(_this.colArr);
        _this.container.height(_max);
    },
    fill: function() {
        /*
         计算填充,
         获取数组最大的值,
         for循环按列循环,判断i是否等于数组中最大值的坐标,不等于的,则设置一个div
         height计算:最大值-数组中保存的top值-margin值
         */
        var _this=this;
        var max=Array.max(_this.colArr);
        var _count=$.inArray(max,_this.colArr);
        var _fillHtml="";
        for(var i=0,len=_this.colNum;i<len;i++) {
            if(i!=_count) {
                _fillHtml+="<div class='goods-fill' style='top:"+_this.colArr[i] +"px;left:"+i*_this.columnOw+"px;height:"+(max-_this.colArr[i]-_this.columnM) +"px;"+"'>"+"</div>";
            }
        }
        _this.context.append(_fillHtml);
    },
    getAjax: function(elem) {
        var _this=this;
        _this.i++;
        $.getJSON(_this.url+_this.i, function(data) {//ajax, json格式 {"status":"","data":""}
            if(data) {
                _this.b=data.status;
                $(elem).append(data.data);
                _this.flow($(elem));
                $(elem).find(_this.elem).appendTo(_this.context);
                if(data.status==true&&_this.i==_this.len) {
                    $(window).unbind("scroll");
 
                    setTimeout( function() {
                        return _this.fill()
                    },100);
                }
            }
        })
    },
    scoll: function(elem) {
        var _this=this,
        _scroll,
        _threshold=_this.threshold;
        _height=parseInt($(window).height());
        $(window).scroll( function() {
            if(_this.b==false&&_this.i<_this.len) {
                _srcoll=parseInt($(this).scrollTop());
                _sTop=_height+_srcoll+_threshold;
                _top=Array.min(_this.colArr);
                if(_top<_sTop) {//alert(1)
                    _this.getAjax(elem);
                }
            }
        })
    }
}
$( function() {
    opts= {
        container:$(".goods-wall"),
        context:$(".goods-container"),
        elem:".goods",
        url:"data.php?k=",
        len:4
    }
    goods=new water(opts)
    goods.init();
})
文章原作者在代码中有注明!