瀑布流這個東西,學過很久了,現在把它記錄一下。
簡單說一下原理:定義一個數組儲存第一行中的所有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
this.count = json.count || ; // 每頁顯示的資料量,預設為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 = ;
for(var i = , len = this.arrHeight.length; i < len; i ++){
if(this.arrHeight[i] < this.arrHeight[min]){
min = i;
}
}
return min;
};
// 擷取最大高度
WaterFall.prototype.getMaxHeight = function(){
var max = ;
for(var i = , 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 - ); // 目前已經顯示了多少資料
for(var i = start; i < this.num; i ++){
var div = document.createElement('div');
div.style.width = this.width + 'px';
div.style.margin = this.gap/ + '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 - ); // 目前已經顯示了多少資料
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 = ;
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();
}
};
};