是開頭都會說的原理
瀑布流佈局有兩種,一種是固定列,一種是非固定列。在此主要記述第一種的實現。
固定列的特徵是:無論頁面如何縮放,每行的總列數都一致。
一行4列的瀑布流從佈局的角度來說,就是4個li標籤。透過一定的事件(例如滾動條滾動多少px),然後讀取之,再把資料動態地加入頁面中。
新增資料原則,不是根據li索引值來加,而是根據各列中高度最短的那列動態添加。否則可能導致頁面很難看(左右高度不統一)。
實例涉及ajax方法。可在伺服器環境下運作。
廢話不多說了。直接上樣式。
<ul id="ul1"> <li> <div> <img src="images/1.jpg" alt="jquery實作簡單的瀑布流佈局" > <p>我是文字描述我是文字描述我是文字描述我是文字描述我是文字描述我是文字描述</p> </div> </li> <li> <div> <img src="images/2.jpg" alt="jquery實作簡單的瀑布流佈局" > <p>我是文字描述我是文字描述我是文字描述我是文字描述我是文字描述我是文字描述</p> </div> </li> <li> <div> <img src="images/3.jpg" alt="jquery實作簡單的瀑布流佈局" > <p>我是文字描述我是文字描述我是文字描述我是文字描述我是文字描述我是文字描述</p> </div> </li> <li> <div> <img src="images/4.jpg" alt="jquery實作簡單的瀑布流佈局" > <p>我是文字描述我是文字描述我是文字描述我是文字描述我是文字描述我是文字描述</p> </div> </li> </ul>
css
*{ margin:0; padding: 0; } ul li{ list-style: none; } #ul1{ width: 1080px; margin: 100px auto 0; } li{ width: 247px; float: left; margin-right: 10px; } li div{ border:1px solid #000;padding:10px; margin-bottom:10px; } li div img{ width: 225px;display: block; }
基本效果如圖:
樣式顯示沒問題之後,就把li裡面的程式碼刪除。
接下來透過ajax來動態加入。
資料哪裡來?
這裡用的是wookmark的資料介面。
http://www.wookmark.com/api/json/popular?page=1
點開url得到是一個json。
資訊量很大。怎麼分析?
一般可以看文件。但是手頭上沒有文件的情況下,可以看看連結。返回是什麼鬼。
function createUrl(num){ return 'http://www.wookmark.com/api/json/popular?page='+num+'&callback=?'; } $(function(){ $.getJSON(createUrl(1),function(data){ console.log(data); }) })
控制台列印結果為:
原來是一個50個圖片資訊組成的陣列。每個數組元素都是一個json。在這個簡單的demo裡面,暫時只需要取preview屬性和title屬性就好了。
佈局實現
關鍵之一在於,判斷最短的li,事實上我們需要最短高度li的索引值。
//找出高度最小li的索引值 function getShortestLi(){ var shortest=0; for(var i=1;i<4;i++){ if($('li').eq(i).height()<$('li').eq(shortest).height()){ shortest=i; } } return shortest; }
然後就是getJSON方法
$(function(){ $.getJSON(createUrl(1),function(data){ //console.log(data); for(var i=0;i<dataArr.length;i++){ var $html=$('<div><img src="'+data[i].preview+'" alt="jquery實作簡單的瀑布流佈局" ><p>'+data[i].title+'</p></div>'); //console.log($('li').eq(getShortestLi()).height()) $('li').eq(getShortestLi()).append($html); }; console.log([$('li').eq(0).height(),$('li').eq(1).height(),$('li').eq(2).height(),$('li').eq(3).height()]) }) })
再載入看看,版面就出來了。簡單又漂亮。
做到這裡,看起來一切都還挺好。然而潛伏著一個致命的問題。
for循環惹的禍?
看看console.log的資訊。為了分析,我把4個li的高度放進了一個陣列:
50張圖片分4列,少說平均高度也得有三四千像素。
而到循環結束,程式判斷的終點竟然只有令人髮指的1000多個px,因為圖片載入過程慢於for迴圈執行速度。雖然demo裡的顯示還算正常,但這種程式碼在網路速度不好時,會造成工作事故。
思路一:可以判斷圖片是否載入完成。
可以用個定時器監聽下,然後用遞歸實現,我的方案是這樣
var index=0; function LoadPic(index){ var $html=$('<div><img src="'+data[index].preview+'" alt="jquery實作簡單的瀑布流佈局" ><p>'+data[index].title+'</p></div>') $('li').eq(getShortestLi()).append($html); var oImg=$html.find('img'); var t=setInterval(function(){ if(oImg.height()!=0){//如果加载完了。 clearInterval(t); //console.log([$('li').eq(0).height(),$('li').eq(1).height(),$('li').eq(2).height(),$('li').eq(3).height()]) if(index<50){ return LoadPic(index+1); }else{ return false; } }else{ console.log('wait') } },50)//每隔50ms监听一次 } LoadPic(0);
但是,從用戶體驗的角度來說,等一張圖加載完成再進行下一張加載是不友好的。資料提供方都應該直接把圖片的高度在伺服器處理好,json資料裡面回傳過來。網速很慢的時候,要等好久,然後一下子圖片都出來了,不覺得很詭異嗎?尤其是第三方介面。一加載不出來就出大問題了。
所幸的是,第三方提供了圖片的寬高資訊。
因此for迴圈還是可以有的,在回傳的資料裡面,有寬度和高度值。利用它們就可以實現定寬(255px)和定高(原始高度乘一個比例)。
$(function(){ $.getJSON(createUrl(1),function(data){ console.log(data); for(var i=0;i<data.length;i++){ //console.log(data[i].preview); var $html=$('<div><img src="'+data[i].preview+'" alt="jquery實作簡單的瀑布流佈局" ><p>'+data[i].title+'</p></div>') $('li').eq(getShortestLi()).append($html); $html.find('img').css('height',(data[i].height*225/data[i].width)+'px'); $html.find('img').css('width','225px'); }; //console.log([$('li').eq(0).height(),$('li').eq(1).height(),$('li').eq(2).height(),$('li').eq(3).height()]) }) })
事實上個人認為這是最簡單且使用者體驗最好的方案。
有了瀑布,還需要流
流的邏輯
往下拉(滾動),第一個底部進入可視區的li,優先加載。
換句話說,最短li的高度與該li到頁面頂部之和小於滾動條高度和可視區高度之和時,觸發li加載。
li的高度好求。但是最短li到頁面頂部距離怎麼求?
原生的方法可以這樣實現:
function getTop(obj){ var iTop=0; while(obj){ iTop+=obj.offsetTop; obj=obj.offsetParent; }//累加元素本身和自身所有父级高度偏移值 return iTop; }
但是本案例既然是用jquery,自然有它的方法。
obj.offset().top
滾動事件
原生的實作方法是:window.onscroll=function(){...}
jquery的實作方法是:$(window).scroll(function( ){...})
現在驗證一下寫出的代碼代碼有沒問題
(window).scroll(function(){ var $li=$('li').eq(getShortestLi()); var scrollTop=document.documentElement.scrollTop||document.body.scrollTop; //console.log([$li.offset().top+$li.height(),document.documentElement.clientHeight+scrollTop]) //如果li高度与li到页面顶部的高度之和<可视区高度+滚动距离 if($li.offset().top+$li.height()<document.documentElement.clientHeight+scrollTop){ alert(1); } })
運行代碼,發現第一個到底的li出現是可視區時,彈出1.證明可用。
因為涉及滾動事件,所以getJSON相關函數應該封裝為getList()方便呼叫。所以需要重新調整一下。
此時的程式碼是這樣的:
//找出高度最小li的索引值 function getShortestLi(){ var shortest=0; for(var i=1;i<4;i++){ if($('li').eq(i).height()<$('li').eq(shortest).height()){ shortest=i; } } return shortest; } function createUrl(num){ return 'http://www.wookmark.com/api/json/popular?page='+num+'&callback=?'; } function getList(n){ $.getJSON(createUrl(n),function(data){ //console.log(data); for(var i=0;i'+data[i].title+'
這樣一來,好像可以實現了。但是一看控制台的console.log,又發現問題。
如廁的邏輯
在触发加载前提时,图片正在加载,期间动了滚动条,就又触发第二次加载,再动一下,就触发第三次,于是短短一瞬间,触发了n次加载。
那就做一个开关吧。
就跟公厕逻辑一样。n个人排队进一个坑位。外面的人想要进去首先得判断门是否锁上了。没锁才能进。进去之后第一件事把门锁上。等如厕完毕,门就打开。后面的人才能进
新设置一个开关bCheck,默认为true。
到触发加载条件时,还要判断bCheck是否为真(门开),为真时才能触发getList()(如厕)。否则return false(只能等)。
getList一开始就把bCheck设为false(如厕前先锁门)。等到getList回调函数执行到尾声。再把bCheck设为true(开门)。
这一段不贴代码了。
总有流完的一天。
当数据结束时(所有人上完厕所),就没有必要再进行加载了(自动把门锁上)。
所以在getJSON回调函数内锁门之后发现进来的是个空数组,那就进行判断,当获取到data的length为空时,直接returnfalse。那么bCheck就永远关上了。
全部代码如下:
//找出高度最小li的索引值 function getShortestLi(){ var shortest=0; for(var i=1;i<4;i++){ if($('li').eq(i).height()<$('li').eq(shortest).height()){ shortest=i; } } return shortest; } function createUrl(num){ return 'http://www.wookmark.com/api/json/popular?page='+num+'&callback=?'; } var bCheck=false; function getList(n){ $.getJSON(createUrl(n),function(data){ if(data.length==0){ return false; }else{ for(var i=0;i'+data[i].title+'
以上就是本文的全部内容,希望本文的内容对大家的学习或者工作能带来一定的帮助,同时也希望多多支持PHP中文网!
更多jquery實作簡單的瀑布流佈局相关文章请关注PHP中文网!