百度圖片以及qq空間等還有一些像蘑菇街 發現啦之類的網站都用的是瀑布流佈局模式,這種模式優點就是合理動態佈局,使得圖片頁面表現有很強的視覺感染力,可以使得客戶快速發現自己喜歡的圖片。
並且大量圖片處理時候減少瀏覽器處理圖片消耗;在操作效果上像瀑布似的嘩嘩的出現圖片 故而得此名(純屬想象)
閒來之餘,實現一個自己的瀑布流demo,發現一些問題 總結一些經驗,統統分享給大家:
先看看效果欣賞一下美圖。。。
實現思路如下:
先把html頁面擺上吧:
<style type="text/css">*{padding:0;margin:0;}
#wrap{
margin:0 auto;
}
#wrap li{
width:220px;
float:left;
list-style:none;
margin:5px;
box-shadow: 0 1px 3px rgba(34,25,25,.4);
background:#eee;
/*margin:5px;*/
/*border:1px solid #ccc;*/
background:#eee;
}
.boxCont{
position:relative;
margin:0,auto;
background:#eee;
min-height: 200px
}
#wrap p{
text-align: center;
height: 30px;
font: 14px 微軟雅黑;
}
#wrap img{
display:block;
position: relative;
z-index: 1;
text-align: center;
margin: 0 auto;
padding: 10px;
max-width: 220px;
background:#eee;
}
</style>
<body>
<ul id="wrap"></ul>
</body>
先定義樣式我把整個ul放置居中位置,定義每列的寬度爲220px間距爲5,兩邊間距加起來就是10了,故而每列佔230px;
1.定義佈局:首先要考慮好佈局:在這裏我根據瀏覽器寬以及定義的每列的寬度以及間距去計算顯示多少列瀑布,計算方法如下:
var liNumx=document.documentElement.offsetWidth/230|0;
$('wrap').style.width=liNumx*230+'px';
2.創建元素:第一次請求數據返回假設只有img路徑,數據格式爲:[{src;'xxx'}...],得到數據後遍歷過程中創建li/div以及img:
var li=document.createElement('li'),div=document.createElement('div'),img=document.createElement('img'),p=document.createElement('p');
div.appendChild(img);li.appendChild(div);this.el.appendChild(li).appendChild(p);
div.className='boxCont';img.setAttribute('data-src',arr[i].src);p.innerHTML="我是第"+(i+1)+"張圖片";
首先給每張圖片都自定義一個data-src屬性並且把img路徑存入這個屬性中(防止圖片呼呼加載造成內存泄露,瀏覽器崩潰);
3:加載圖片:
加載圖片時候我遍歷請求返回的所有數據,根據步驟2創建的元素去判斷每個元素是否在當前屏幕內(我一開始定義元素樣式時候就給li默認定義一個最小高度,所以參加判斷的是li,不必等到圖片加載完再去判斷),
如果在屏幕內則將img的data-src值賦給img的src,並且移除data-src屬性,然後給img添加一個已經加載了的標誌屬性sign值爲true;
ajaximgs.forEach(function(image,i){
var src = image.getAttribute('data-src');if(src && !!that.isInScreen(image.parentNode.parentNode)){
image.src=src;
image.setAttribute('sign',true);
image.removeAttribute('data-src');//排序時機代碼
}
});
4:元素排序時機選擇:
ul中第一行元素不參與排序,所以我們要用到步驟三以及1中的已加載的img個數與列數去作對比,大於列數再參加排序,這裏排序的時機有三個選擇:
第一個選擇是不進行任何判斷,直接參與排序(考慮到排序實際跟本身寬高無關)
第二個選擇是等到圖片加載完,然後讓去參與排序,
第三個選擇是設置一個定時器每隔一段時間就去排序(當加載完畢後就取消定時器);
我們來一個個進行分析:
第一種方案顯然是最直接的處理方式,不過這種方式在處理時候並不能得到正確的已經加載的圖片的元素高度,因爲在執行到的時候上一個或者上上個圖片還沒有加載完成,
那麼上一個或者上上個的li高度也依然是默認的高度,這時候排序就不能獲得正確的已加載的最小高度列。故而不可取(如果在執行時候已經知道圖片高度可以採用此方法:例如從ajax返回每個圖片寬高數據);
第二種方案要等到圖片加載完之後再去排序,這種方案如果碰上像我這樣本地加載的 是肯定沒問題的,但是一般都是網絡請求地址,服務器不是一個,這樣請求圖片加載時間就不好說了,有的幾十毫秒有的幾秒鐘都有可能,
所以如果遇到幾秒鐘的其他的圖片要等到這個圖片加載完成才能參與排序,這樣就造成了用戶對着空白的li等待時間過長;所以一般這個也不可取(不過如果圖片服務器和網站同一個情況下有可能是沒問題的,具體還要視需求,
如果根本不在乎客戶體驗 ,那無所謂了。。)
第三種方案,也就是我折中採用的所謂最佳方案,設置一個定時器幾十毫秒執行一次排序,幾十毫秒一般圖片都會加載完成,如果沒有加載完成也可以將其忽略,不會影響剩餘的等待參與排序;
代碼如下:
that.intervalId=setInterval(that.sort.bind(that,that.el),40);
這是寫到類裏面的其中that是類作用域,sort爲具體排序:
5:實現排序:
排序實現思路是這樣的,我們得到頁面上所有已經加載img的li,然後對其進行遍歷,如果是第一行則不進行排序,其餘的進行排序
定義一個數組存放所有此次請求返回的li,得到每列的寬度以及每個li的高度
sort:function(el){
if(!this.imgs){
this.stop();//取消定時
return;
}
var h = [];
var box = el.getElementsByTagName("li");
var minH, //最小列高度
boxW = box[0].offsetWidth+10, boxH,
n = document.documentElement.offsetWidth / boxW | 0;
for(var i = 0; i < box.length; i++) {boxh = box[i].offsetHeight;
if(i < n&&this.startAjax==1) {
h[i] = boxh;box[i].style.position = '';
}else {
minH = Math.min.apply({},h); //取得各列累計高度最低的一列
minKey = getkey(h, minH);
h[minKey] += boxh ; //加上新高度後更新高度值box[i].style.position = 'absolute';
box[i].style.top = minH + 'px';
box[i].style.marginLeft = (minKey * boxW)+5 + 'px';}
}
}
這段核心sort排序代碼中,我遍歷頁面所有li統統進行排序,第一次請求的第一行圖片不進行排序,其餘之後的再進行排序。根據每列最小值將新圖片放置該列。
缺點: 這樣的做法再加上定時進行排序是沒問題的,不過在這裏我是遍歷所有元素進行排序,這樣就大大降低了排序效率。
想過對於每次請求的數據分別進行排序,不過這個想法由於圖片加載順序一直不能倒騰出來,有待優化。大家如果有什麼好建議可以分享一下,不勝感激。
ok,核心思想就是這些,具體操作起來有些細節還是要深究的,在這不方便都把代碼全貼上了;
下載地址:js瀑布流