html結(jié)構(gòu)
(Emmet)
(div.box>div.pic>img[src="images/$.jpg"])*23
? ? ? ? ? ?
? ? ? ? ? ?
? ? ? ? ? ?
? ? ? ? ? ?
css結(jié)構(gòu)
*{
? ? margin:0;
? ? padding:0;
}
#main{
? ? position: relative;
}
.box{
? ? padding:10px 0 0 15px;
? ? /*display: inline-block;一行顯示*/
? ? float: left;
}
.pic{
? ? padding: 10px;
? ? border:1px solid #ccc;
? ? border-radius: 5px;
? ? box-shadow: 0 0 5px #ccc;
}
.pic img{
? ? /*瀑布流特點*/
? ? width: 165px;
? ? height: auto;
}
一、JavaScript原生方法實現(xiàn)瀑布流布局
整個功能封裝在waterfall()函數(shù)中。
圖片定位
js文件是在head中引入,所以執(zhí)行的腳本需要放在window.onload事件中。
因為我們進行操作的是main下的box元素,首先要先進行獲取元素。
然鵝,js中沒有提供專門獲取Class名的元素的方法。又為了方面后面的函數(shù)的調(diào)用,我們將獲取父元素main下的所有class為box的子元素的這個功能進行封裝getByClass()。
window.onload = function () {
? ? ? waterfall('main','box');
}
function waterfall(parent,box){
? ? //把父元素main下所有class名為box的子元素都取出來
? ? var oParent = document.getElementById(parent);
? ? getByClass(oParent,box);
}
getByClass()實現(xiàn)的思路:若要獲取父元素下所有class為特定名的子元素,首先要把父元素下所有子元素全部都取出來,然后進行遍歷,判斷每個子元素上的className是否和你傳入的class名相等;如果相等,這個子元素就是我們要找的,然后把這些我們要找的子元素存儲起來boxArr。
這里,通過Class取到的所有元素,最后的結(jié)果是數(shù)組類型(不止一個)。
獲取到的元素要存儲起來,boxArr.push()向數(shù)組的末尾添加元素。
//根據(jù)class獲取元素
function getByClass(parent,clsName){
? ? var boxArr = new Array(),//用來存儲獲取到的所有class為box的元素
? ? ? ? oElements = parent.getElementsByTagName('*');//取出父元素下的所有子元素
? ? for (var i = 0; i < oElements.length; i++) {
? ? ? ? if(oElements[i].className == clsName){
? ? ? ? ? ? boxArr.push(oElements[i]);
? ? ? ? }
? ? }
? ? return boxArr;
}
getByClass()函數(shù)最后返回的是一個數(shù)組,var oBoxs = getByClass(oParent,box);聲明一個變量oBoxs來接收獲取到的所有元素。console.log(oBoxs.length);// 23 說明已經(jīng)全部取出
這樣就把ID為main的父元素下所有class為box的子元素都取出來了。
問題:
oElements[i].className == className? ,用這種方法判斷,欠妥當(dāng)。
因為現(xiàn)實項目中,className 不止一個,這樣就永遠沒法相等,應(yīng)該這樣判斷
oElements[i].className.indexOf(className) > 0
--
功能點
瀏覽器窗口大小變化時,頁面中一行里圖片的個數(shù)是固定的。即圖片列數(shù)不隨瀏覽器窗口大小的變化而變化。
思路:圖片列數(shù)是固定值→使大盒子main的寬度值固定即可→圖片的列數(shù)×一個box的寬
3個步驟:
確定列數(shù):以當(dāng)前的頁面寬度,除以一個 box 的寬度,結(jié)果取整{Math.floor()}
確定 main 容器的寬度:列數(shù)(即每行中能容納box的個數(shù))乘以一個 box 的寬(也可以這樣寫:oParent.style.width = oBoxW*cols+'px';)
定位第一行盒子:將 box 集合作為數(shù)組取出,遍歷子元素,加入入數(shù)組
一個box的寬=圖片寬度165+內(nèi)邊距10×2+邊框1×2 +填充15
在waterfall()中執(zhí)行以下代碼,設(shè)置main的寬度以及對齊方式。使用cssText屬性以字符串的形式對其設(shè)置。offsetWidth計算的是沒有外邊距的盒子寬。
//計算整個頁面顯示的列數(shù)(頁面寬/box的寬)
? ? var oBoxW = oBoxs[0].offsetWidth;//等寬
? ? // console.log(oBoxW);
? ? var cols = Math.floor(document.documentElement.clientWidth / oBoxW)//取整;
? ? // console.log(cols);
? ? //設(shè)置main的寬
? ? oParent.style.cssText = 'width:'+oBoxW*cols+'px;margin:0 auto;'
圖片排序(盒子排列)
3個步驟:
找到上一行里高度最小的盒子(即空隙最大的地方)
把要排列隊列里的第一個的盒子定位到這個空白處
(需要兩個數(shù)值,第一個是上一行最矮盒子的高度【方法:Math.min.apply()】,第二個是上一行最矮盒子的左邊距【兩種辦法:盒子寬最矮盒子下標;數(shù)組里最小盒子的offsetLeft?!? *
更新這一列的高度,最矮元素的高,加上當(dāng)前盒子的高度
--
arrayObject.push() 方法可向數(shù)組的末尾添加一個或多個元素,并返回新的長度。
Math.min()返回的是一組數(shù)據(jù)中的最小值。
Math.min.apply(null,hArr)取數(shù)組中的最小值;apply()接收兩個參數(shù),一個是函數(shù)運行的作用域(this),第一個參數(shù)是null的情況下,this指向window,另一個是參數(shù)數(shù)組。
--
求最矮盒子的下標這里寫出getMinhIndex(hArr,minH)函數(shù),或者使用hArr.indexOf(minH)直接返回下標值。
getMinhIndex(hArr,minH)這里一旦找到這個最小值,就返回了索引號;若有多個最小高度值相等,那么返回的是這個隊列中第一個出現(xiàn)的最小高度值。
//遍歷數(shù)組中的每一個值,若與傳入的特定值相等,返回該下標值。
function getMinhIndex(arr,val){
? ? ? ? for(var i in arr){
? ? ? ? ? ? if(arr[i]==val){
? ? ? ? ? ? ? ? return i;
? ? ? ? ? ? }
? ? ? ? }
? ? }
這樣就找到了上一行最矮的那個盒子以及該盒子所在的索引號。接下來就對下一行第一個盒子進行絕對定位。這時候發(fā)現(xiàn)剩下的所有盒子重疊了。這是因為上一行所有圖片的高度hArr[],是固定的那幾個值,所以求得的最小值minH是固定的。即后面的所有盒子都堆在了這個固定最矮圖片的下面。
解決:修改數(shù)組hArr[]里的高度值→改變最小高度值hArr[Index] += oBoxs[i].offsetHeight。最矮元素的高,加上當(dāng)前盒子的高度,更新這一列的高度。
//waterfall()函數(shù)中執(zhí)行。
//先把上一行圖片的高度全都取出來,然后進行存儲。再找出圖片高度的最小值以及該圖片所在的索引對下一個盒子進行絕對定位。
var hArr = [];//存放每一列高度的數(shù)組
? ? for (var i = 0; i < oBoxs.length; i++) {
? ? ? ? if (i
? ? ? ? ? ? //或hArr[i]=oBoxs[i].offsetHeight;
hArr.push(oBoxs[i].offsetHeight);? ? //獲取第一行盒子的高度并進行存放
}else{
//下一行里第一張圖片的定位
var minH = Math.min.apply(null,hArr);//獲取最小值// console.log(minH);
var Index = getMinhIndex(hArr,minH);//獲取索引值
//var Index = hArr.indexOf(minH);
oBoxs[i].style.position = 'absolute';
oBoxs[i].style.top = minH + 'px';//把圖片加上一行中最矮的圖片的底下
oBoxs[i].style.left = oBoxs[Index].offsetLeft + 'px';//px
//修改hArr中的最小值
hArr[Index] += oBoxs[i].offsetHeight;
? ? ? ? }
? ? ? ? console.log(hArr);
? ? }
圖片加載功能
思路:
何時加載:滾動條x向上滾動的距離(scrollTop)與可視區(qū)頁面高度(clientHeight)之和 大于最后一張圖片的距離父元素頂端位置(offsetTop)與盒子高度(offsetHeight )的一半之和。
offsetTop+offsetHeight / 2是固定值,滾動條向下滾動的距離和頁面向上偏離的距離相等,知道滾到當(dāng)前隊列中最后一張圖片自身高度的一半或者該圖片剛顯露出來(自定義)開始加載其他的圖片。滾動條向下滾動的距離和頁面的可視區(qū)高度若小于這個固定值,說明沒有滾到需要加載圖片的時候。
怎么加載:json數(shù)據(jù)交換格式;創(chuàng)造元素并進行嵌套(appendChild()方法 語法:parent.appendChild(children))將數(shù)據(jù)信息渲染到頁面中。
由于數(shù)據(jù)都是從后臺來,這里模擬json格式的數(shù)據(jù);首先進行遍歷,然后創(chuàng)建盒子,塞到main盒子里。遍歷給出的數(shù)據(jù),將圖片添加到數(shù)據(jù)塊中渲染出來
首先實現(xiàn)何時加載功能checkScrollSlide().找出當(dāng)前隊列中的最后一個盒子oBoxs[oBoxs.length - 1],計算數(shù)值時一般計算機能接受的最小單位是像素,即整數(shù),所以求盒子自身高度值的一半用到Math.floor()。
返回的是布爾型。是否加載。
//監(jiān)測是否具備滾動加載數(shù)據(jù)塊的條件
function checkScrollSlide(){
? ? ? ? var oParent = document.getElementById('main');
? ? ? ? var oBoxs = getByClass(oParent,'box');
? ? ? ? var lastBoxH = oBoxs[oBoxs.length - 1].offsetTop + Math.floor(oBoxs[oBoxs.length - 1].offsetHeight / 2);
? ? ? ? // console.log(lastBoxH);最后一個盒子到頁面頂部的距離+自身高度的一半
? ? ? ? var scrollTop = document.body.scrollTop || document.documentElement.scrollTop;//滾動條向上滾動的距離
? ? ? ? // console.log(scrollTop);
? ? ? ? var height = document.body.clientHeight || document.documentElement.clientHeight;//頁面可視區(qū)高度
? ? ? ? // console.log(height);
? ? ? ? return (scrollTop+height>lastBoxH)?true:false;//三元操作符
? ? ? ? // return scrollTop+height>lastBoxH;
? ? }
用到window.onscroll()滾動條滾動事件,在頁面加載完畢后即window.onload()里觸發(fā)。
dataInt是一個對象,以數(shù)組的形式存放數(shù)據(jù)信息。data為數(shù)據(jù)屬性。dataInt.data.length數(shù)據(jù)的個數(shù)。
數(shù)據(jù)加載進來后,只是這些要渲染的數(shù)據(jù)只是被加到頁面中,并沒有進行圖片定位和圖片排序。會出現(xiàn)重疊和圖片間有空白等現(xiàn)象。這時,需要再調(diào)用一下waterfall()函數(shù)。
這樣,頁面中滾動條不斷下拉時,dataInt對象里幾張圖片就會不斷的進行加載。
var dataInt = {"data":[{"src":'0.jpg'},{"src":'1.jpg'},{"src":'2.jpg'},{"src":'3.jpg'}]};//模擬后臺數(shù)據(jù)
window.onscroll = function () {
? ? ? ? if (checkScrollSlide) {//為真
? ? ? ? ? ? //將數(shù)據(jù)塊渲染到頁面的尾部
? ? ? ? ? ? var oParent = document.getElementById('main');
? ? ? ? ? ? //遍歷數(shù)據(jù)塊
? ? ? ? ? ? for(var i=0;i
? ? ? ? ? ? ? ? //創(chuàng)建存放數(shù)據(jù)塊的盒子并渲染到頁面中
? ? ? ? ? ? ? ? var oBox = document.createElement('div');
? ? ? ? ? ? ? ? oBox.className = 'box';
? ? ? ? ? ? ? ? oParent.appendChild(oBox);
? ? ? ? ? ? ? ? var oPic = document.createElement('div');
? ? ? ? ? ? ? ? oPic.className ='pic';
? ? ? ? ? ? ? ? oBox.appendChild(oPic);
? ? ? ? ? ? ? ? var oImg = document.createElement('img');
? ? ? ? ? ? ? ? //獲取數(shù)據(jù)塊中的文件名
? ? ? ? ? ? ? ? oImg.src="images/"+dataInt.data[i].src;//都存放在固定文件images里
? ? ? ? ? ? ? ? oPic.appendChild(oImg);
? ? ? ? ? ? }
? ? ? ? ? ? waterfall('main','box');
? ? ? ? }
}
問題:
checkScrollSlide調(diào)用的問題
函數(shù)只要是要調(diào)用它進行執(zhí)行的,都必須加括號。此時,函數(shù)()實際上等于函數(shù)的返回值。當(dāng)然,有些沒有返回值,但已經(jīng)執(zhí)行了函數(shù)體內(nèi)的行為,這個是根本,就是說,只要加括號的,就代表將會執(zhí)行函數(shù)體代碼。
不加括號的,都是把函數(shù)名稱作為函數(shù)的指針,用于傳參,此時不是得到函數(shù)的結(jié)果,因為不會運行函數(shù)體代碼。它只是傳遞了函數(shù)體所在的地址位置,在需要的時候好找到函數(shù)體去執(zhí)行。
二、JQuery實現(xiàn)瀑布流布局
$(window).on('load',function(){
? ? waterfall();
? ? var dataInt = {"data":[{"src":'0.jpg'},{"src":'1.jpg'},{"src":'2.jpg'},{"src":'3.jpg'}]};
? ? $(window).on('scroll',function(){
? ? ? ? if(checkScrollSlide){
? ? ? ? ? ? //創(chuàng)建盒子并添加到頁面中
? ? ? ? ? ? $.each(dataInt.data,function(key,value){
? ? ? ? ? ? ? ? var oBox=$('
? ? ? ? ? ? ? ? var oPic=$('
// console.log(value);value是dataInt里的對象,即原生js對象,需加$裝換成JQuery對象才能使用JQuery方法
var oImg=$('').attr('src','images/'+$(value).attr('src')).appendTo($(oPic));
})
waterfall();
}
})
});
function waterfall(){
? ? var $boxs=$('#main>div');//獲取main下的一級div元素
? ? var w = $boxs.eq(0).outerWidth();//一個盒子的寬度包括填充和邊框
? ? var cols = Math.floor($(window).width()/w);
? ? $('#main').width(w*cols).css('margin','0 auto');//設(shè)置main的寬度以及對齊方式
? ? var hArr = [];
? //數(shù)組遍歷
? ? $boxs.each(function(index,value){
? ? ? ? // console.log(index);
? ? ? ? // console.log(value);DOM對象
? ? ? ? var h =$boxs.eq(index).outerHeight();
? ? ? ? if(index
? ? ? ? ? ? hArr[index]=h;
? ? ? ? }else{
? ? ? ? ? ? var minH = Math.min.apply(null,hArr);//最小值
? ? ? ? ? ? var minHIndex = $.inArray(minH,hArr);//索引
? ? ? ? ? ? //console.log(value);value是DOM對象,需加$裝換成JQuery對象才能使用JQuery方法
? ? ? ? ? ? $(value).css({
? ? ? ? ? ? ? ? 'position':'absolute',
? ? ? ? ? ? ? ? 'top':minH+'px',
? ? ? ? ? ? ? ? 'left':minHIndex*w+'px'
? ? ? ? ? ? })
? ? ? ? ? ? hArr[minHIndex]+=$boxs.eq(index).outerHeight();//更新數(shù)組
? ? ? ? }
? ? })
? ? // console.log(hArr);
}
function checkScrollSlide(){
? ? var $lastBox=$('#main>div').last();//獲取最后一個元素
? ? var lastBoxDis=$lastBox.offset().top+Math.floor($lastBox.outerHeight()/2);
? ? var scrollTop=$(window).scrollTop();//滾動條滾動的距離
? ? var documentH=$(window).height();//頁面可視區(qū)高度
? ? return (lastBoxDis
}
三、CSS3實現(xiàn)瀑布流布局
根據(jù)盒子的寬度設(shè)置column-width屬性。這里一個box的寬=圖片寬度165+內(nèi)邊距10×2+邊框1×2 +填充15
這種方式不需要計算,只需要設(shè)置列寬,瀏覽器自動計算,性能高。但是列寬會隨著瀏覽器窗口的大小進行改變,用戶體驗不好;另外圖片排序是按照垂直順序排列的。最后圖片的加載需要JavaScript實現(xiàn)。
#main{
? ? /*position: relative;*/
? ? -webkit-column-width: 202px;
? ? -moz-column-width: 202px;
? ? -o-column-width: 202px;
? ? column-width: 202px;
}
? ? ? ?
? ? ? ?
? ? ? ?
? ? ? ?