淘宝有部分页面中使用了瀑布流布局,淘宝的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();
})
文章原作者在代码中有注明!


