瀑布流这个东西,学过很久了,现在把它记录一下。
简单说一下原理:定义一个数组保存第一行中的所有div的高度(如果有间隙,加上间隙),找出第一行中的最小高度,然后下一行第一个div的top值就等于这个最小高度,然后更新保存的最小高度,继续重复执行。
如果我们要将它做成小插件的话,需要考虑:
1. 我们要将它放到什么地方去?
2. 当前显示的是第几页
3. 每页显示多少数量
4. jsonp返回的数据
5. 每个div的宽度
6. div与div之间的间隙
7. 传入进来的图片的key值
8. 如果有文字说明,传入进来的文本的key值
以上几点都是我们要考虑的,因为这些可能是会变化的,所以这些都要做成变量,让用户传入。
好了,以下附上源码(还有点小问题,但我还不知哪里出问题了)。
/**
* 瀑布流
* @param {Object} json:数据
*/
function WaterFall(json){
this.obj = json.obj; // 瀑布流要在哪个对象中显示
this.page = json.page || 1; // 当前的页数,默认为1
this.count = json.count || 10; // 每页显示的数据量,默认为10
this.data = json.data; // 利用jsonp获取的数据
this.width = json.width; // 每张图片的宽度
this.gap = json.gap; // 图片与图片之间的间隙
this.path = json.path; // 利用jsonp返回的数据中的图片路径的key值,不是value值
this.textName = json.textName; // 文本描述,如果没有文本,可以不写或者传入可以转换为false的值
this.arrHeight = []; // 记录一行中每个div高度的数组
this.divs = []; // 装着产生的div
this.num = this.page * this.count; // 当前显示的图片的总数
this.isEnd = false; // 是否到了利用jsonp获取的最后一个数据,如果到了,就算继续滚动鼠标滚轮,也不会再加载任何数据
}
// 初始化方法
WaterFall.prototype.init = function(){
this.setObj();
this.createElements();
this.updatePosition();
this.mouseEnd();
};
// 主要用于设置传入进来的对象的position属性
WaterFall.prototype.setObj = function(){
this.obj.style.position = 'relative';
};
// 获取最小高度
WaterFall.prototype.getMinHeight = function(){
var min = 0;
for(var i = 0, len = this.arrHeight.length; i < len; i ++){
if(this.arrHeight[i] < this.arrHeight[min]){
min = i;
}
}
return min;
};
// 获取最大高度
WaterFall.prototype.getMaxHeight = function(){
var max = 0;
for(var i = 0, len = this.arrHeight.length; i < len; i ++){
if(this.arrHeight[i] > this.arrHeight[max]){
max = i;
}
}
return max;
};
// 创建div、创建img
WaterFall.prototype.createElements = function(){
var start = this.count * (this.page - 1); // 当前已经显示了多少数据
for(var i = start; i < this.num; i ++){
var div = document.createElement('div');
div.style.width = this.width + 'px';
div.style.margin = this.gap/2 + 'px';
div.style.position = 'absolute';
var img = document.createElement('img');
img.src = this.data[i][this.path];
img.style.width = this.width + 'px';
div.appendChild(img);
// 如果有传入文本,则创建p标签
if(this.textName){
var p = document.createElement('p');
p.innerHTML = this.data[i][this.textName];
div.appendChild(p);
}
this.divs.push(div);
}
};
// 更新div的位置
WaterFall.prototype.updatePosition = function(){
// 计算一行能放多少个div
var cols = parseInt(this.obj.offsetWidth / (this.width + this.gap));
// 计算更新位置的起始值(上一次的结束位置)
var start = this.count * (this.page - 1); // 当前已经显示了多少数据
for(var i = start; i < this.num; i ++){
var d = this.divs[i];
this.obj.appendChild(d); // 将创建的div添加到对象中
if(i < cols){ // 计算并设置第一行的div的left
d.style.top = 0;
d.style.left = i * (this.width + this.gap) + 'px';
// 将第一行中每个div的(高度+间隙)添加到数组中
this.arrHeight.push(d.offsetHeight + this.gap);
}else{
var min = this.getMinHeight(); // 获得每一行中的最小高度的div的下标
d.style.top = this.arrHeight[min] + 'px';
d.style.left = min * (this.width + this.gap) + 'px';
// 更新数组中保存的最小高度,上一行的最小高度加上目前div的高度及间隙
this.arrHeight[min] += (d.offsetHeight + this.gap);
}
}
// 设置最外层obj的高度值
var max = this.getMaxHeight();
this.obj.style.height = this.arrHeight[max] + 'px';
};
// 鼠标滚动到底部时,页数加1,继续加载剩余的数据,当数据加载完成,不再继续加载
WaterFall.prototype.mouseEnd = function(){
var t = this;
window.onscroll = function(){
var scrollTop = document.documentElement.scrollTop || document.body.scrollTop;
var clientHeight = document.documentElement.clientHeight || document.body.clientHeight;
if(scrollTop + clientHeight > t.obj.offsetHeight){
if(t.isEnd){
return;
}
// 当当前页面的总数据量大于jsonp传递过来的数据量时,将isEnd设置为true
// 并将页面总数据量设置为jsonp传递过来的数据量
// 当前页数设置为jsonp传递过来的数据量除以每页数据,并向上取整
if(t.num >= t.data.length){
t.isEnd = true;
t.num = t.data.length;
t.page = Math.ceil(t.data.length / t.count);
}else{
// 如果当前页面的总数据量小于或等于jsonp传递过来的数据量时,
// 当前页数加1,更新当前页面总数据量
t.page++;
t.num = t.count * t.page;
// 如果当前页面的总数据量刚好等于jsonp传递过来的数据量时,也将isEnd设置为true
if(t.num == t.data.length){
t.isEnd = true;
}
}
// 根据页面总数据量创建节点及更新节点位置
t.createElements();
t.updatePosition();
}
};
};