導讀:瀑布流,又稱瀑布流式佈局。是比較流行的一種網站頁面佈局,視覺表現爲良莠不齊的多欄佈局,隨着頁面滾動條向下滾動,這種佈局還會不斷加載數據塊並附加至當前尾部。最先採用此佈局的網站是Pinterest,逐漸在國內流行開來。國內大多數清新站基本爲這類風格,像花瓣網、蘑菇街、美麗說等。html
改進版的代碼見:github,能夠與現有的進行對比,見文章末尾。git
最近在好多地方看到瀑布流的字眼,感受真的很不錯,因而就想本身能不能寫一個呢,並且是響應式的。通過將近兩天的研究,終於寫出來了,先傳幾張圖給你們看看最終的效果:github
隨着瀏覽器頁面的大小調整。佈局從四列逐漸變成三列兩列,甚至是一列(圖像的塊的寬度是保持不變的)。數組
在我看來,在複雜的程序其實都是由很簡單的函數組合在一塊兒的,另外有一些常常用到的部分也會被抽取出來當作一個新的函數。而後,爲了封裝,會將一些變量也封裝起來,而後經過外部傳遞進來,最後就成了咱們如今看到的。其實一開始,不多人可以肯定要怎麼寫,且能肯定須要傳進哪些參數。一開始只能大概肯定須要作哪些,須要哪些函數,寫着寫着,發現缺了在添加就行了。至少我本身是這樣的(也許是我如今太渣了)。瀏覽器
好了,接下來講一說 具體實現的過程。請結合源碼來看。微信
第一步:咱們須要建立這些圖像塊,也就是源碼中的createDiv函數。這裏爲了節省性能,咱們採用字符串的形式,而不用createElement等這些函數。而後這裏你會發現一個問題,咱們生成的塊的大小是同樣的。這時候,咱們就須要用到random函數了。經過該函數來生成高度不同的圖像塊。dom
第二步:怎樣肯定圖像的列數和怎樣讓他隨着頁面的改變而改變本身的列數。這裏咱們就得用到widthchange函數。首先圖像塊的父容器的外邊距是20px;所以總的寬度就得減去40(這裏以瀏覽器頁面來算)。而後再根據圖像塊的寬度,來肯定每一列之間的列數和間距。ide
第三步:設置樣式setStyle。圖像塊的大小肯定以後,咱們得設置圖像塊的樣式,好比大小,間距等等。函數
第四步:把上面的結合起來,進行封裝就行了。佈局
-------------------------------------------------------------------------------------------------------------------
可是呢,咱們得考慮一個問題,就是圖片太多了,咱們不可能一次讓全部圖片都下載,這樣會影響網頁的性能和用戶的體驗。那麼有沒有什麼好的辦法呢?咱們只讓可視區的圖片顯示就行了。那怎麼實現呢,咱們來分析下
一、這裏涉及到上面的createDiv函數,得先說清楚。咱們得將img的src的地址設爲其餘地址或爲空。而後再自定義一個data-src屬性,屬性值爲圖片的地址。
二、可視區的圖片顯示。也就是咱們得先判斷圖片的位置是否是在這個可視區內,這裏就得用到pageY,show函數,這些函數會用到offsetTop,scrollTop和clientHeight等這些屬性。
三、若是在的話,就將data-src的值賦給src,而且將其從數組中去掉,表示該圖片已經被加載。
四、最後將函數進行修改,封裝。再綁定好各個事件就大功告成了。
最後附上源碼供你們研究,若是以爲好的話,能夠加個人我的微信公衆號,這樣你就不用訪問博客就能夠獲取相關信息。
公衆號名字:JavaScript學習與實踐
微信號:learnpracticeJs
公衆號二維碼:
歡迎你們關注個人公衆號,你的關注就是我最大的支持。
源代碼:
<!DOCTYPE html> <html> <head> <meta http-equiv="Content-Type" content="text/html; charset=utf-8" /> <title>瀑布流 by huansky</title> <style> #container{ position: relative; top:20px; margin: 20px 20px; background: #c00; } #container .wf{ position: absolute; border: 1px solid #ccc; font-size: 40px; border-radius: 10px; overflow: hidden; } p{ margin: 0px; padding: 0px; } h1{ text-align: center; } </style> </head> <body> <h1>瀑布流 by huansky</h1> <div id="container">54454</div> </body> <script > function wf(obj){ this.colHeight=[]; //每一列的高 this.colLeft=[]; //每一列的位置; 由於列是等寬的,因此每一列的位置也是固定的。 this.flag=0; //標誌位,包含圖片的容器只須要建立一次 this.imgWidth=obj.wth; //設置每一列的寬度 this.id=document.getElementById(obj.id); //獲取最外層的容器 this.classname=document.getElementsByClassName(obj.classname); //獲取瀑布流的類名 //變量必定要在init()以前給定義,不能放在init()以後 this.init(); //初始化 } wf.prototype={ //獲取m到n的隨機值 random:function(m,n){ return Math.ceil(Math.random()*(n-m)+m); }, //最小值的下標 getMinCol:function(arr){ var ca = arr,cl = arr.length,temp = ca[0],minc = 0; for(var ci = 0; ci < cl; ci++){ if(temp > ca[ci]){ temp = ca[ci]; minc = ci; } } return minc; }, //獲取頁面的大小,從而調整列數。 widthchange:function(){ winWidth = document.body.clientWidth; //獲取頁面的寬度 //console.log("ttt "+document.documentElement.clientHeight); var cols=Math.floor((winWidth-40)/this.imgWidth); //40是頁面的兩邊的邊距 //console.log(this.imgWidth); var colsmar=Math.floor((winWidth-40-20*(cols+1))/this.imgWidth); //列數 //console.log(colsmar); var cmargin=Math.floor(winWidth-this.imgWidth*colsmar-40)/(colsmar+1); //每一列的間距 //console.log(cmargin); for(var i=0;i<colsmar;i++){ this.colHeight[i]=20; this.colLeft[i]=(i+1)*cmargin+i*this.imgWidth; } }, //建立頁面視圖 createDiv:function(m){ var div=""; for(var n=0; n<m;n++){ var height=this.random(150,350); div+="<div class='wf' style=height:"+height+"px;width:"+this.imgWidth+"px;><p>"+(n+1)+"</p><img src='img/loading.gif' data-src='img/"+(n+1)+".png' style=height:"+(height-45)+"px;width:"+this.imgWidth+"px;></div>"; } this.id.innerHTML=div; }, //設置每一個小塊的樣式,邊距等等 setStyle:function(){ for(var i=0;i<50;i++){ var lowcol=this.getMinCol(this.colHeight); this.classname[i].style.left=this.colLeft[lowcol]+"px"; this.classname[i].style.top=this.colHeight[lowcol]+"px"; this.colHeight[lowcol]=this.classname[i].offsetHeight+this.classname[i].offsetTop+20;//offsetTop是數值 } }, init:function(){ if(!this.flag){ //只建立小塊一次,否則會每次都得從新加載圖片 this.createDiv(50); this.flag=1;//設置flag=1,這樣下次就不會加載了 } this.widthchange(); //newContainer.init(); this.setStyle(); } } //只加載在可視區內的圖片 function LazyLoad(id){ this.container=document.getElementById(id); //獲取id this.imgs=this.getImgs(); //獲得img數組 this.init(); } LazyLoad.prototype={ init:function(){ this.update(); }, getImgs:function(){ var arr=[]; var imgs=this.container.getElementsByTagName("img"); for (var i=0,len=imgs.length;i<len;i++){ arr.push(imgs[i]); } return arr; }, update:function(){ if(!this.imgs.length){ //console.log(this.imgs.length); return; } var i=this.imgs.length; for(--i;i>=0;i--){ if (this.show(i)){ this.imgs[i].src=this.imgs[i].dataset.src; this.imgs.splice(i,1); //console.log(newContainer.imgs[i].dataset.src); } } }, //顯示圖片,斷定圖片是否在但是區域內。 show:function(i){ var img=this.imgs[i], scrollTop=document.documentElement.scrollTop||document.body.scrollTop, scrollBottom=scrollTop+document.documentElement.clientHeight, imgTop=this.pageY(img), imgBottom=imgTop+img.offsetHeight; //若是知足,讓他顯示。 if(imgBottom>scrollTop && imgBottom<scrollBottom || (imgTop>scrollTop && imgTop<imgBottom)) return true; return false; }, //獲取圖片的最高點的y座標 pageY:function(element){ if(element.offsetParent){ return element.offsetTop+this.pageY(element.offsetParent); }else{ return element.offsetTop; } } } //var dom=document.getElementById("container"); //var cName=document.getElementsByClassName("wf"); //綁定事件函數 function on(element,eventName,listener){ if (element.addEventListener){ element.addEventListener(eventName,listener,false); } else if (element.attachEvent){ element.attachEvent('on'+eventName,listener); } else element['on'+eventName]=listener; } var newWf=new wf({wth:300,id:"container",classname:"wf"});//建立瀑布流 var newContainer=new LazyLoad("container"); //惰性加載函數 on(window,"resize",function(){ newWf.init(); newContainer.update(); }) //綁定scroll事件 on(window,"scroll",function(){ newContainer.update(); }) </script> </html>