瀑布流已經火了一段時間了,自己最近研究了一下,網上關於瀑布流的帖子也很多,網上一般是說三種方式,固定列數的浮動布局,CSS3列布局,絕對定位布局
推薦兩個關於瀑布流的帖子,寫得比我詳細多了:
迅雷的:http://cued.xunlei.com/log031
張鑫旭的:網址太長點我
這里主要是記錄一下我的實現方式,用數組模擬的數據,也可以用AJAX實現讀取數據,底部提供DEMO代碼下載!
一、固定列數的浮動布局
這種方式簡單適用,先按照列數把布局固定好,然后在滾動事件中分別在每一列插入相應的數據既可,代碼比較簡單:
其實就一個滾動加載事件。我這里沒有做按高低排序。
代碼如下:

<!DOCTYPE html> <html> <head> <meta charset="utf-8"> <title>瀑布流布局(基於固定寬度的浮動定位)</title> <link rel="stylesheet" href="style/style.css"> </head> <body> <div id="warp" class="warp"> <div class="full" id="row1"> <div class="water"> <a href="javascript:void(0)"><img src="images/01.JPG" alt=""></a> <p class="title">瀑布流布局</p> </div> <div class="water"> <img src="images/02.JPG" alt=""> <p class="title">瀑布流布局</p> </div> <div class="water"> <img src="images/09.JPG" alt=""> <p class="title">瀑布流布局</p> </div> <div class="water"> <img src="images/15.JPG" alt=""> <p class="title">瀑布流布局</p> </div> </div> <div class="full" id="row2"> <div class="water"> <img src="images/03.jpg" alt=""> <p class="title">瀑布流布局</p> </div> <div class="water"> <img src="images/04.JPG" alt=""> <p class="title">瀑布流布局</p> </div> <div class="water"> <img src="images/10.JPG" alt=""> <p class="title">瀑布流布局</p> </div> <div class="water"> <img src="images/08.JPG" alt=""> <p class="title">瀑布流布局</p> </div> </div> <div class="full" id="row3"> <div class="water"> <img src="images/05.JPG" alt=""> <p class="title">瀑布流布局</p> </div> <div class="water"> <img src="images/06.JPG" alt=""> <p class="title">瀑布流布局</p> </div> <div class="water"> <img src="images/11.JPG" alt=""> <p class="title">瀑布流布局</p> </div> <div class="water"> <img src="images/14.JPG" alt=""> <p class="title">瀑布流布局</p> </div> </div> <div class="full last" id="row4"> <div class="water"> <img src="images/07.JPG" alt=""> <p class="title">瀑布流布局</p> </div> <div class="water"> <img src="images/08.JPG" alt=""> <p class="title">瀑布流布局</p> </div> <div class="water"> <img src="images/12.JPG" alt=""> <p class="title">瀑布流布局</p> </div> <div class="water"> <img src="images/13.JPG" alt=""> <p class="title">瀑布流布局</p> </div> </div> </div> <script type="text/javascript" src="style/waterfull.js"></script> </body> </html>
JS代碼:

/* * * 基於固定寬度的浮動定位的瀑布流 * 實現簡單,其實就是一個滾動加載數據而已 * 缺點布局不隨寬度的變化而改變,如果有圖片特別長的時候,最高的列與最低的列有可能差距大,空白大 * by VVG http://www.cnblogs.com/NNUF/ */ var WaterFull = { $:function(id){return document.getElementById(id);}, // 每次滾動需要加載的數據,可以用ajax替代讀取,每次分批加載 data:[{imgUrl:'images/01.jpg',link:'javascript:void(0)',title:'瀑布流浮動定位01'}, {imgUrl:'images/02.jpg',link:'javascript:void(0)',title:'瀑布流浮動定位02'}, {imgUrl:'images/03.jpg',link:'javascript:void(0)',title:'瀑布流浮動定位03'}, {imgUrl:'images/05.jpg',link:'javascript:void(0)',title:'瀑布流浮動定位04'}, {imgUrl:'images/06.jpg',link:'javascript:void(0)',title:'瀑布流浮動定位05'}, {imgUrl:'images/07.jpg',link:'javascript:void(0)',title:'瀑布流浮動定位06'}, {imgUrl:'images/08.jpg',link:'javascript:void(0)',title:'瀑布流浮動定位07'}, {imgUrl:'images/09.jpg',link:'javascript:void(0)',title:'瀑布流浮動定位08'}, {imgUrl:'images/10.jpg',link:'javascript:void(0)',title:'瀑布流浮動定位09'}, {imgUrl:'images/11.jpg',link:'javascript:void(0)',title:'瀑布流浮動定位10'}, {imgUrl:'images/12.jpg',link:'javascript:void(0)',title:'瀑布流浮動定位11'}, {imgUrl:'images/13.jpg',link:'javascript:void(0)',title:'瀑布流浮動定位12'}, {imgUrl:'images/14.jpg',link:'javascript:void(0)',title:'瀑布流浮動定位13'}, {imgUrl:'images/15.jpg',link:'javascript:void(0)',title:'瀑布流浮動定位14'} ], createChild:function(link,imagesUrl,title){ var str = '<a href="' + link + '"><img src="' + imagesUrl + '"></a>' + '<p class="title">' + title + '</p>'; var div = document.createElement('div'); div.className = 'water'; div.innerHTML = str; return div; }, //綁定事件 on:function(element, type, func) { if (element.addEventListener) { element.addEventListener(type, func, false); //false 表示冒泡 } else if (element.attachEvent) { element.attachEvent('on' + type, func); } else { element['on' + type] = func; } }, //獲取列高度,返回數組,從小到大排序 getRowByHeight:function(){ var row = [this.$('row1'),this.$('row2'),this.$('row3'),this.$('row4')]; var height = []; for(var i = 0;row[i];i++){ row[i].height = row[i].offsetHeight; height.push(row[i]); } // 對高度進行排序,低--》高,保證最矮的優先加載 height.sort(function(a,b){ return a.height - b.height; }); return height; }, //獲取頁面總高度(總高度 = 卷去高度 + 可視區域高度) getPageHeight:function(){ return document.documentElement.scrollHeight || document.body.scrollHeight ; }, // 獲取頁面卷去的高度 getScrollTop:function(){ return document.documentElement.scrollTop || document.body.scrollTop; }, // 獲取頁面可視區域寬度 getClientHeigth:function(){ return document.documentElement.clientHeight || document.body.clientHeight; }, append:function(){ var i = 0,rows = this.getRowByHeight(),div,k; for(;this.data[i];i++){ div = this.createChild(this.data[i].link, this.data[i].imgUrl,this.data[i].title); // 因為是4列,所以數據以4列一個輪回加載 k = ((i+1)>4)?i%4:i; // 在列上添加數據 rows[k].appendChild(div); } }, onScroll:function(){ // 獲取高度等數據 var height = WaterFull.getPageHeight(); var scrollTop = WaterFull.getScrollTop(); var clientHeight = WaterFull.getClientHeigth(); // 如果滾動到最底部,就加載 if(scrollTop + clientHeight > height - 50){ WaterFull.append(); } }, timer:null }; WaterFull.on(window, 'scroll',function(){ clearTimeout( WaterFull.timer ); //清除上一次,性能優化 WaterFull.timer = setTimeout(WaterFull.onScroll,500); });
二、絕對定位實現的瀑布流,寬度自適應,resize重排
絕對定位實現方式因為要獲取每格的高度,然后根據高度來計算定位位置,所以必須要知道圖片的高度,如果沒有高度,定位就不准確,所以傳遞數據的時候需要圖片的高度。
我是這樣計算絕對定位的位置的,left根據列數來,這個略過,下面主要講TOP值的獲取:
比如如下的排列方式,數字代表格子
0 1 2 3 4 5
6 7 8 9 10 11
12 13 14 15 16 17
第12格子的絕對定位TOP值 = 第0格的高度(offsetHeight) + 相隔距離(margin) + 6格的高度(offsetHeight) + 相隔距離(margin);
這里可以把每格的高度通過對象的屬性記錄下來,以免每次都進行offsetHeight的獲取,我的代碼里面沒有體現出來。
這個計算就通過一個循環來實現:
計算定位的代碼如下:
function sort(){ var num = getColumnNum(), left, top, column; //nowNum的作用是不讓已經加載的數據重新計算定位排列 for (var j = nowNum, k = cells.length; j < k; j++, nowNum++) { // 初始化top的值 top = 0; // 獲取當前為第幾列 column = j < num ? j : j % num; // 計算可以得到當前列的LEFT值 left = column * (cellClientWidth + columnMarginRight); cells[j].style.left = left + 'px'; if (j < num) { // 第一列top值為0 cells[j].style.top = '0px'; } else { // 計算TOP值,等於當前格子的頂上每列的高度相加 for (var m = column; m < j; m = m + num) { top = top + cells[m].offsetHeight + columnMarginRight; } cells[j].style.top = top + 'px'; } } }
瀑布流絕對定位總代碼如下:
<!DOCTYPE html> <html> <head> <meta charset="utf-8"> <title>瀑布流布局(絕對定位)</title> <style type="text/css"> html, body{ height:100% } html, body, #warp p{ margin:0; padding:0 } #warp{ margin:20px auto; position:relative; min-height:1000px; } #warp .cell{ padding:10px; border:1px solid #ccc; box-shadow:2px 2px 5px #ccc; overflow:hidden; } #warp .cell a{ text-decoration:none; color:#878787; font:14px/1.5em Microsoft YaHei; } img{ border:none; } </style> </head> <body> <div id="warp" class="warp clearfix"></div> <script type="text/javascript"> var data = [ {imgUrl:'images/01.jpg', link:'javascript:void(0)', title:'瀑布流絕對定位01', height:273}, {imgUrl:'images/02.jpg', link:'javascript:void(0)', title:'瀑布流絕對定位02', height:144}, {imgUrl:'images/03.jpg', link:'javascript:void(0)', title:'瀑布流絕對定位03', height:168}, {imgUrl:'images/04.jpg', link:'javascript:void(0)', title:'瀑布流絕對定位04', height:275}, {imgUrl:'images/05.jpg', link:'javascript:void(0)', title:'瀑布流絕對定位05', height:288}, {imgUrl:'images/06.jpg', link:'javascript:void(0)', title:'瀑布流絕對定位05', height:272}, {imgUrl:'images/07.jpg', link:'javascript:void(0)', title:'瀑布流絕對定位06', height:285}, {imgUrl:'images/08.jpg', link:'javascript:void(0)', title:'瀑布流絕對定位07', height:282}, {imgUrl:'images/09.jpg', link:'javascript:void(0)', title:'瀑布流絕對定位08', height:190}, {imgUrl:'images/10.jpg', link:'javascript:void(0)', title:'瀑布流絕對定位09', height:236}, {imgUrl:'images/11.jpg', link:'javascript:void(0)', title:'瀑布流絕對定位10', height:225}, {imgUrl:'images/12.jpg', link:'javascript:void(0)', title:'瀑布流絕對定位11', height:264}, {imgUrl:'images/13.jpg', link:'javascript:void(0)', title:'瀑布流絕對定位12', height:144}, {imgUrl:'images/14.jpg', link:'javascript:void(0)', title:'瀑布流絕對定位13', height:192}, {imgUrl:'images/15.jpg', link:'javascript:void(0)', title:'瀑布流絕對定位14', height:343} ]; var waterFull = function (options) { var id = options.id, picWidth = options.picWidth || 190, columnPadding = options.columnPadding || 10, columnBorder = options.columnBorder || 1, columnMarginRight = options.columnMargin || 20, // 格子總寬度 cellClientWidth = picWidth + columnPadding * 2 + columnBorder * 2, obody = document.getElementsByTagName('body')[0], owarp = document.getElementById(id), // 用於記錄當前插入的格子數量 nowNum = 0, cells = []; // 用於記錄每個單獨層對象 // 獲取列數 function getColumnNum() { // 根據每列的寬度來計算總共的列數 var columnNum = Math.floor(obody.clientWidth / (cellClientWidth + columnMarginRight)); // 然后再設置owarp的寬度,是其保持居中 owarp.style.width = columnNum * (cellClientWidth + columnMarginRight) - columnMarginRight + 'px'; return columnNum; } // 創建格子 function createCell(left, top, link, imgUrl, imgHeight, title) { var cssText = 'position:absolute;left:' + left + 'px;top:' + top + 'px'; var inHTML = '<a href="' + link + '" target="_blank"><img src="' + imgUrl + '" alt="' + title + '" height="' + imgHeight + 'px"><p class="title">' + title + '</p></a>'; //console.log(inHTML); var div = document.createElement('div'); div.className = 'cell'; div.style.cssText = cssText; div.innerHTML = inHTML; return div; } // 插入數據 function insert(data) { var fragElement = document.createDocumentFragment(); if (data.length > 0) { for (var i = 0, n = data.length; i < n; i++) { var cell = createCell(-9999, -9999, data[i].link, data[i].imgUrl, data[i].height, data[i].title); fragElement.appendChild(cell); cells.push(cell); } owarp.appendChild(fragElement); } // 插入后再排序 sort(); } //排序 function sort(){ var num = getColumnNum(), left, top, column; //nowNum的作用是不讓已經加載的數據重新計算定位排列 for (var j = nowNum, k = cells.length; j < k; j++, nowNum++) { // 初始化top的值 top = 0; // 獲取當前為第幾列 column = j < num ? j : j % num; // 計算可以得到當前列的LEFT值 left = column * (cellClientWidth + columnMarginRight); cells[j].style.left = left + 'px'; if (j < num) { // 第一列top值為0 cells[j].style.top = '0px'; } else { // 計算TOP值,等於當前格子的頂上每列的高度相加 for (var m = column; m < j; m = m + num) { top = top + cells[m].offsetHeight + columnMarginRight; } cells[j].style.top = top + 'px'; } } } // resize 重新排列 function resort() { // 設置nowNum=0即可重排 nowNum = 0; // 重排 sort(); } // 暴露接口 return { insert:insert, resort:resort } }; var tool = { on:function (element, type, func) { if (element.addEventListener) { element.addEventListener(type, func, false); //false 表示冒泡 } else if (element.attachEvent) { element.attachEvent('on' + type, func); } else { element['on' + type] = func; } }, getPageHeight:function () { return document.documentElement.scrollHeight || document.body.scrollHeight; }, // 獲取頁面卷去的高度 getScrollTop:function () { return document.documentElement.scrollTop || document.body.scrollTop; }, // 獲取頁面可視區域寬度 getClientHeigth:function () { return document.documentElement.clientHeight || document.body.clientHeight; }, timer:null, timer2:null }; var myWaterFull = waterFull({id:'warp'}); // 初始化的數據 myWaterFull.insert(data); tool.on(window, 'scroll', function () { clearTimeout(tool.timer); //清除上一次,性能優化 tool.timer = setTimeout(function () { var height = tool.getPageHeight(); var scrollTop = tool.getScrollTop(); var clientHeight = tool.getClientHeigth(); // 如果滾動到最底部,就加載 if (scrollTop + clientHeight > height - 50) myWaterFull.insert(data); }, 500); }); tool.on(window, 'resize', function () { clearTimeout(tool.timer2); tool.timer2 = setTimeout(function () { myWaterFull.resort(); }, 500) }) </script> </body> </html>
DEMO下載:點擊下載