waterfall瀑布流布局+動(dòng)態(tài)渲染的實(shí)現(xiàn)

瀑布流典型網(wǎng)站
花瓣網(wǎng)、堆糖
瀑布流布局原理
大體思路
首先先是頁面布局 特點(diǎn)是:寬度一樣,長度不一樣
由此可以知道,這種布局要用到 絕對(duì)定位 的思想來做。
上面的五個(gè)正常排列,到了第六個(gè)以后就要找最矮的追加了。
如何獲取最矮的一列呢?
第一個(gè)最好找,其他的每一個(gè)盒子可以獲取它的高度,找最矮的盒子,然后找到最矮盒子的定位。
新追加進(jìn)去的盒子的定位是:
- left:最矮盒子的索引*(盒子的寬度+左右間距)
- top: 這個(gè)盒子的高度 + 上下間距
放進(jìn)去之后這一列的高度變化,記錄下來生成新的高度,然后進(jìn)行下一輪高度的比較。以此類推。
waterful
是一個(gè)組件,基于 jquery
的一個(gè)組件。
具體思路
最外邊的左右兩邊是沒有間距的,所以 5
列的情況下有 4
個(gè)間距。所以寬度 width
一定的情況下,間距的寬度 space
是可以計(jì)算出來的:
間距
var space = (wParent - 5 * width) / (col - 1); // wParent 父盒子的寬度,width是子盒子的寬度,col是列數(shù)
第一排的盒子的定位:
- top : 0
- left : 索引*(width + space)
第二排的盒子的定位:
- top : minHeight + space
- left : 索引*(width + space)
所以 5
列的高度要用一個(gè)數(shù)組表示,可以找到最矮的元素以及其當(dāng)前的索引。
插件封裝
因?yàn)樵诘谝淮渭虞d和第 n
次加載的時(shí)候,都要進(jìn)行瀑布流布局。所以將瀑布流布局的方法進(jìn)行一個(gè)插件進(jìn)行封裝,可以形成代碼的復(fù)用。 首先了解瀑布流的 html
布局
<!--頁面容器--> <div class = "container"> <!--所有item的集合,距離頂部有距離--> <div class = "items"> <!--每一個(gè)小塊,包含了圖片和文字--> <div class = "item"> <img src = "" /> <p>hello</p> </div> <div class = "item"> <img src = "" /> <p>hello</p> </div> </div> </div> <div class = "btn">正在加載...</div>
下面就來封裝一個(gè) jquery
的插件
第一步
將 jquery
中的全局變量轉(zhuǎn)化為局部變量。
防止全局污染,提高性能 形成一個(gè)閉包,閉包里面定義的變量是不會(huì)影響外部變量的。
/*自調(diào)用 形成一個(gè)閉包 */ (function($){ /*如果不加jQuery里面使用的$是全局變量,加了之后使用的就是成員變量*/ })(jQuery);
第二步
jquery.fn.extend(object)
jquery
中的 fn
函數(shù)
提供一個(gè)第三方方法的一個(gè)入口,擴(kuò)展 jquery
元素集(使用 $
可以獲取到的元素) 來提供新的方法(通常用來制作插件)
/*js/jquery-waterfall.js*/ (function($){ $.fn.waterfall = function(){ /*this指向的是當(dāng)前調(diào)用這個(gè)方法的元素集(元素集是jquery獲取元素是一個(gè)偽數(shù)組)*/ console.log(this); } })(jQuery);
第三步
對(duì)第一排進(jìn)行排列
(function($){ $.fn.waterfall = function(){ // this指向的是當(dāng)前調(diào)用這個(gè)方法的元素集 // 當(dāng)前的瀑布流父容器 var items = $(this); //父容器的寬度 var wParent = items.width(); //當(dāng)前的瀑布流子容器 var child = items.children(); //獲取子容器的寬度 var width = child.width(); //假設(shè)排多少列 var col = 5; //計(jì)算間距(父元素的寬度減所有盒子的寬度/4) var space = (wParent - col * width) / (col - 1); //記錄每列高度的數(shù)組 var colHeightArr = []; //遍歷每一個(gè)子元素 $.each(child,function(i,item){ var $item = $(item); var height = $item.height(); //設(shè)置定位 //第一排的元素都是靠頂部的,所以索引從0開始,小于5的時(shí)候都是靠頂部的 if(i < col ){ $item.css({ top: 0, left:i * (width + space) }); //把高度添加進(jìn)數(shù)組中 colHeightArr[i] = height; //也可以用 colHeightArr.push(height); } //其他的都要根據(jù)最矮的一列進(jìn)行排列 }); } })(jQuery);
這樣你就看到了效果圖(因?yàn)槟M做了 13
個(gè)盒子,所以剩下的疊在了一起)
這個(gè)時(shí)候打印以下高度數(shù)組:
可以看到前 5
個(gè)的高度都存到數(shù)組中去了。可以判斷出來數(shù)組中最小的是 289
, 289
對(duì)應(yīng)的數(shù)組的索引就是那一列的索引。
第四步
對(duì)其余的排進(jìn)行排列。找最小的追加,然后本列的高度增加。以此類推。 最大的 items
等于最大的高度。這樣才可以把下面的加載移動(dòng)到下邊去。
(function($){ $.fn.waterfall = function(){ // this指向的是當(dāng)前調(diào)用這個(gè)方法的元素集 // 當(dāng)前的瀑布流父容器 var items = $(this); //父容器的寬度 var wParent = items.width(); //當(dāng)前的瀑布流子容器 var child = items.children(); //獲取子容器的寬度 var width = child.width(); //假設(shè)排多少列 var col = 5; //計(jì)算間距 var space = (wParent - col * width) / (col - 1); //記錄每列高度的數(shù)組 var colHeightArr = []; //遍歷每一個(gè)子元素 $.each(child,function(i,item){ var $item = $(item); var height = $item.height(); //定位 //第一排的元素都是靠頂部的 //索引從0開始,小于5的時(shí)候都是靠頂部的 if(i < col ){ $item.css({ top: 0, left:i * (width + space) }); //colHeightArr[i] = height; colHeightArr.push(height); //其他的都要根據(jù)最矮的一列進(jìn)行排列 }else{ //找到最矮的那一列進(jìn)行排列 //索引 var index = 0; //假設(shè)最小的高度是第一個(gè)索引對(duì)應(yīng)的高度 var minHeight = colHeightArr[index]; //遍歷數(shù)組,找到最小值和最小值對(duì)應(yīng)的索引 //k是索引,v是值 $.each(colHeightArr,function(k,v){ if(minHeight > v){ index = k; minHeight = v; } }); //定位 $item.css({ top:minHeight + space, left:index * (width + space) }) //當(dāng)前數(shù)組中最小的高度進(jìn)行新的高度的更新 colHeightArr[index] = minHeight + space + height; } //console.log(colHeightArr); }); //設(shè)置父容器的高度 var maxHeight = colHeightArr[0]; $.each(colHeightArr,function(k,v){ if(maxHeight < v){ maxHeight = v; } }); //給父容器設(shè)置最高的高度 items.height(maxHeight); } })(jQuery);
效果圖:
第五步
html
中調(diào)用(上面的效果圖都是已經(jīng)調(diào)用過的)
$(".items").waterfall();
但是如果有圖片的話,這樣調(diào)用在網(wǎng)絡(luò)比較慢的情況下會(huì)出現(xiàn)問題。在圖片沒有加載出來的時(shí)候排列,中間圖片加載完畢會(huì)造成盒子重疊的效果。
解決辦法:
/*頁面上所有的資源都加載完成后進(jìn)行布局,否則獲取不到圖片的尺寸撐不開盒子的高度*/ window.onload = function(){ $(".items").waterfall(); } //為什么不用jquery的,因?yàn)檫@個(gè)是在dom元素下載完畢之后進(jìn)行加載這個(gè)方法,需要等所有的資源加載完之后進(jìn)行排列 /* $(function(){ //console.log('dom loaded'); }); */
動(dòng)態(tài)渲染
因?yàn)閿?shù)據(jù)很多,所以會(huì)進(jìn)行分批次渲染。
原理圖:
接口文檔:
接口說明: 瀑布流分頁數(shù)據(jù)
接口地址:data.php
請(qǐng)求方式:get
接口參數(shù):page 當(dāng)前是第幾頁
pageSize 當(dāng)前頁要顯示多少條
返回類型:json
返回?cái)?shù)據(jù):
{ page:2,items:[{path:"./images/1.jpg",text:'''},...] }
page 下一頁的頁碼(根據(jù)頁碼獲取下一頁的數(shù)據(jù))
items 返回當(dāng)前頁的數(shù)據(jù)
path 圖片地址
text 文字
此時(shí)我們要準(zhǔn)備好殼子
<div class="container"> <div class="items"> <!--TODO 需要渲染數(shù)據(jù)的地方--> </div> <div class="btn loading">正在加載中...</div> </div>
需求分析
加載第一頁的時(shí)候
1.加載第一頁的數(shù)據(jù) ajax
2.按鈕需要顯示成加載更多
3.加載完成渲染到頁面當(dāng)中 artTemplate
4.初始化成瀑布流布局 waterfall
加載下一頁的時(shí)候
1.加載數(shù)據(jù)
- 手動(dòng)加載:點(diǎn)擊按鈕加載下一頁的數(shù)據(jù)
- 自動(dòng)加載:滾動(dòng)到底部的時(shí)候主動(dòng)加載下一頁
2.按鈕需要顯示 “正在加載中...”
不能點(diǎn)擊 防止重復(fù)提交
3.加載完成渲染到頁面當(dāng)中
4.初始化成瀑布流布局
5.按鈕需要顯示成加載更多
沒有更多數(shù)據(jù)把按鈕禁用 顯示 “沒有更多數(shù)據(jù)了”
渲染第一頁數(shù)據(jù)
發(fā)送請(qǐng)求
既然加載頁面的時(shí)候都會(huì)用到加載數(shù)據(jù)、渲染頁面、初始化瀑布流,就把這三個(gè)動(dòng)能封裝到一個(gè)函數(shù)中去,先實(shí)現(xiàn)第一個(gè)功能:
$(function(){ //實(shí)現(xiàn)動(dòng)態(tài)的瀑布流渲染 //渲染 var render = function(){ // 加載數(shù)據(jù) 渲染頁面 瀑布流布局 $.ajax({ type:'get', url:'data.php', data:{ //第一頁 page:1, //每頁10條 pageSize:10 }, dataType:'json', success:function(data){ console.log(data); } }); } render(); });
拿到的數(shù)據(jù)如圖:
渲染頁面
準(zhǔn)備模板
<script type="text/template" id="template"> <% for(var i=0 ; i<items.length ; i++){ %> <div class="item"> <img src="<%=items[i].path%>" alt=""> <p><%=items[i].text%></p> </div> <% } %> </script> <script> $(function(){ //獲取需要操作的dom var $items = $(".items"); var $btn = $(".btn"); //渲染 var render = function(){ // 加載數(shù)據(jù) 渲染頁面 瀑布流布局 $.ajax({ type:'get', url:'data.php', data:{ page:1, pageSize:10 }, dataType:'json', success:function(data){ console.log(data); $items.append(template('template',data)); //瀑布流布局 $items.waterfall(); //更改按鈕 $btn.removeClass('loading').html('加載更多'); } }); } render(); }); </script>
第二頁面的渲染(手動(dòng)加載)
第二頁需要改變的東西:
- 添加按鈕的點(diǎn)擊事件,點(diǎn)擊按鈕之后就進(jìn)行渲染。
- 點(diǎn)擊按鈕加載的時(shí)候,要給按鈕加鎖,因?yàn)椴患拥脑挄?huì)發(fā)送多個(gè)ajax請(qǐng)求,判斷按鈕是不是loading狀態(tài),如果是的話就不渲染數(shù)據(jù)。
- render函數(shù)中,在進(jìn)行按鈕狀態(tài)改變的時(shí)候,用自定義屬性記錄下來下一頁的要獲取的頁數(shù)。利用data(),里面?zhèn)饕粋€(gè)page,把data.page放進(jìn)去。所以在拿數(shù)據(jù)的時(shí)候,要從按鈕的data中獲取page的值。第一次是空的,所以就設(shè)定一個(gè)默認(rèn)值為1
- render函數(shù)中在數(shù)據(jù)成功加載之前,按鈕還是loading狀態(tài),所以加一個(gè)beforeSend的函數(shù),里面是loading狀態(tài)。
- render函數(shù)中在渲染的時(shí)候判斷一下是不是沒有數(shù)據(jù)了,根據(jù)返回的數(shù)組中的長度是不是為零來判斷,如果是零的話就顯示沒有更多數(shù)據(jù)了。
$(function(){ //獲取需要操作的dom var $items = $(".items"); var $btn = $(".btn"); //渲染 var render = function(){ // 加載數(shù)據(jù) 渲染頁面 瀑布流布局 $.ajax({ type:'get', url:'data.php', data:{ //取下一頁的頁碼,沒有的話就默認(rèn)是1 page:$btn.data("page")||1, //每頁10條 pageSize:10 }, beforeSend:function(){ $btn.addClass("loading").html('正在加載中...'); }, dataType:'json', success:function(data){ console.log(data); //準(zhǔn)備模板 //因?yàn)槭亲芳铀圆荒苡胔tml,要用append //直接用data的原因是因?yàn)閐ata本來就是一個(gè)對(duì)象,里面有很多的屬性,而不是一個(gè)數(shù)組,數(shù)組的話不能這樣,因?yàn)閿?shù)據(jù)只有l(wèi)ength一個(gè)屬性 $items.append(template('template',data)); //瀑布流布局 $items.waterfall(); if(data.items.length){ //更改按鈕 //data是一個(gè)自定義屬性,把數(shù)據(jù)中傳輸出來的page保存在自定義屬性當(dāng)中, $btn.data("page",data.page).removeClass('loading').html('加載更多'); }else{ //沒有更多數(shù)據(jù) //判斷什么時(shí)候就沒有數(shù)據(jù)了,打開最后的一個(gè)對(duì)象,里面items的數(shù)組的長度為零 $btn.addClass("loading").html("沒有更多數(shù)據(jù)了"); } } }); } //按鈕加載 $btn.on('click',function(){ //避免發(fā)送多個(gè)ajax請(qǐng)求,就進(jìn)行判斷,如果是loading狀態(tài),就退出, if($btn.hasClass("loading")){ return false; } render(); }) render(); });
第二頁面的渲染(滾動(dòng)加載)
說到滾動(dòng)渲染,就是要我們渲染過的頁面到瀏覽器底部的一定距離就要進(jìn)行下一次的請(qǐng)求了,這就要進(jìn)行一個(gè)判斷了。
原理圖:
當(dāng) bottom < 200px
的時(shí)候進(jìn)行 ajax
請(qǐng)求。
其中 bottom
要怎么計(jì)算?
bottom = items的高度 + items距離頂部的距離 - 向上卷曲的高度 - 整個(gè)瀏覽器的高度
$(function(){ //實(shí)現(xiàn)動(dòng)態(tài)的瀑布流渲染 //獲取需要操作的dom var $items = $(".items"); var $btn = $(".btn"); //渲染 var render = function(){ // 加載數(shù)據(jù) 渲染頁面 瀑布流布局 $.ajax({ type:'get', url:'data.php', data:{ page:$btn.data("page")||1, pageSize:10 }, beforeSend:function(){ $btn.addClass("loading").html('正在加載中...'); }, dataType:'json', success:function(data){ console.log(data); $items.append(template('template',data)); //瀑布流布局 $items.waterfall(); //判斷數(shù)組中有沒有數(shù)據(jù) if(data.items.length){ $btn.data("page",data.page).removeClass("loading").html('加載更多'); }else{ $btn.addClass("loading").html("沒有更多數(shù)據(jù)了"); } } }); } //滾動(dòng)加載 $(window).on('scroll',function(){ //文檔距離底部的距離小于200px 去加載 //并且加載完成才能繼續(xù)加載 //items的高度 var itemsHeight = $items.height(); //items距離頂部的偏移量 var itemsTop = $items.offset().top; //整個(gè)頁面距離頂部的卷曲出去的距離 var scrollTop = $(document).scrollTop(); // 瀏覽器的高度 var winHeight = $(window).height(); // 瀏覽器底部距離items底部的距離 var bottom = itemsHeight + itemsTop - scrollTop -winHeight; // 判斷按鈕是不是loading狀態(tài) var loading = $btn.hasClass("loading"); //如果按鈕小于200 且 不是loading狀態(tài)就開始加載 if(bottom < 200 && !loading){ render(); } }) render(); });
需要特別注意的問題
之前我們?cè)陟o態(tài)加載頁面的時(shí)候使用的是 window.onload
,是為了讓頁面上的資源全部加載完成之后再進(jìn)行頁面的渲染。否則就會(huì)產(chǎn)生頁面重疊的現(xiàn)象。
在動(dòng)態(tài)加載頁面的時(shí)候,我們先拿到后臺(tái)的數(shù)據(jù),然后轉(zhuǎn)化成 html
追加到頁面上之后,才開始加載 img
圖片。這里也遇到了之前的問題。
之所以后面沒有用 window.onload
那樣的做,是因?yàn)樵驹O(shè)置的圖片已經(jīng)設(shè)定可寬和高。 img
有的設(shè)定了 250px
,有的設(shè)定了 450px
。 但是這樣做不合理,因?yàn)橛械膱D片會(huì)變形。
下面提供解決問題的方法:
- 等所有的圖片加載完成進(jìn)行頁面渲染,但是這樣時(shí)間會(huì)比較長,不合理。
- 參考花瓣
花瓣在加載圖片的時(shí)候也進(jìn)行了寬高的設(shè)定,但是這個(gè)大小要根據(jù)原圖片的尺寸進(jìn)行大小的縮放。
原來的尺寸是 608
,現(xiàn)在的寬度是 200
,那么現(xiàn)在的高度就進(jìn)行一個(gè)換算
現(xiàn)在的高度 = 200 / 806 * 782
width
是現(xiàn)在的寬度
// 模板引擎中寫 <img height = "<%=items[i].width * items[i].height / width%>" src = "<%=items[i].path%>" /> /* 同樣在ajax的success中 $items.append( template('template',{ data:data, width:width }) ); 這樣width變量就可以使用了。 */
這樣瀑布流就完成了.
到此這篇關(guān)于waterfall瀑布流布局+動(dòng)態(tài)渲染的實(shí)現(xiàn)的文章就介紹到這了,更多相關(guān)waterfall瀑布流布局內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章,希望大家以后多多支持腳本之家!
相關(guān)文章
- 這篇文章主要介紹了3種方式實(shí)現(xiàn)瀑布流布局小結(jié),文中通過示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)2019-09-05
詳解純css實(shí)現(xiàn)瀑布流(multi-column多列及flex布局)
這篇文章主要介紹了詳解純css實(shí)現(xiàn)瀑布流(multi-column多列及flex布局)的相關(guān)資料,用multi-column多列布局及flex布局實(shí)現(xiàn)瀑布流,感興趣的小伙伴們可以參考一下2018-09-04css3 column實(shí)現(xiàn)卡片瀑布流布局的示例代碼
這篇文章主要介紹了css3 column實(shí)現(xiàn)卡片瀑布流布局的示例代碼的相關(guān)資料,小編覺得挺不錯(cuò)的,現(xiàn)在分享給大家,也給大家做個(gè)參考。一起跟隨小編過來看看吧2018-06-22- 這篇文章主要介紹了用CSS3實(shí)現(xiàn)瀑布流布局的示例代碼,小編覺得挺不錯(cuò)的,現(xiàn)在分享給大家,也給大家做個(gè)參考。一起跟隨小編過來看看吧2017-11-10
CSS3實(shí)現(xiàn)瀑布流布局與無限加載圖片相冊(cè)的實(shí)例代碼
本篇文章主要介紹了CSS3實(shí)現(xiàn)瀑布流布局與無限加載圖片相冊(cè)的實(shí)例代碼,具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下。2016-12-22瀑布流布局的兩種實(shí)現(xiàn)方式:傳統(tǒng)多列浮動(dòng)和絕對(duì)定位布局
瀑布流布局想必大家對(duì)它并不陌生吧,在一些網(wǎng)站上都會(huì)有這種效果的出現(xiàn),下面為大家介紹下使用兩種方式實(shí)現(xiàn)傳統(tǒng)多列浮動(dòng)和絕對(duì)定位布局,具體的實(shí)現(xiàn)代碼如下,感興趣的朋友2013-08-26