
网站备案终于下来了,等了十几天,不容易呀,顺便搞了个免费的https证书,把评论也换成畅言的了,来必力太挫了,ui看见想打人系列。
瀑布流,又称瀑布流式布局。其特点是随着页面滚动条向下滚动,不断加载数据块并附加至当前尾部。从Pinterest开始兴起,国内也有许多网站采用这种样式,不过大多数为了节约代码采用的是等宽等高的样式。今天我要演示的是等宽不等高如何进行瀑布流加载。
html结构
由于我们这里瀑布流的布局是采用绝对定位的,所以html结构非常简单,加载的html都是通过js操控的。
| 1 2 3 4 5 6 7 8 9
 | <main>         <div id="main">                 <div class="pin">       //js生成的                     <div class="box">                         <img src="./images/0.jpg">                     </div>                 </div>          </div>  </main>
 | 
css
css没什么好说的,也是非常简单,由于其等宽不等高的性质,我们把.box的宽度都定死,这里由于要整体居中,由于.pin有padding-left:14px;,故而而把#main设置了left: -7px;让其整体居中一下。
| 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21
 | *{padding: 0;margin:0;} #main{     position: relative;     left: -7px; } .pin{     padding: 14px 0 0 14px;     float:left; } .box{     padding: 10px;     border:1px solid #ccc;     box-shadow: 0 0 6px #ccc;     border-radius: 5px;     width: 182px;     box-sizing: border-box; } .box img{     width:100%;     height:auto; }
 | 
js
瀑布流的重点部分就在js啦。
首先我们写一个创建节点的函数,用于创建.pin、.box和img,createDom传入父节点和img的src。
| 1 2 3 4 5 6 7 8 9 10 11
 | function createDom(obj,src){           var oDiv = document.createElement('div');           oDiv.className = 'pin';           var oBox = document.createElement('div');           oBox.className = 'box';           var oImg = document.createElement('img');           oImg.src = './images/'+src;           oBox.appendChild(oImg);           oDiv.appendChild(oBox);           obj.appendChild(oDiv);       }
 | 
此时我们的页面上还空无一物呢,这时需要我们模拟后端数据,遍历json调用createDom生成dom节点。
| 1 2 3 4 5 6 7 8
 | var oParent = document.getElementById('main');      var dataInt = [];      for(var i=0;i<98;i++){          dataInt.push({'src':i+'.jpg'});          if(i<50){              createDom(oParent,dataInt[i].src);            }      }
 | 
此时样式大致是这样的

然后我们再写一个定位的函数,让其一一队列排齐。
由于是等宽的,我们获取其中一个.pin的宽度,然后让可视窗口宽度除以这个宽度,通过向下取整获得一行能放下.pin的个数。用.pin的宽度*个数得到它父容器应该有的宽度赋给父容器,然后让其居中显示,此时整体就居中了。
我们再把所有的.pin遍历一遍,先把第一行的所有.pin的高度装在pinHarr数组里面。由于瀑布流是一一对齐的,每一行的第一个都是在最矮的那一列下面。
所以第二行的第一个.pin应该在它的上一行最矮的那个.pin下面,所以不是第一行的话,我们给其全部绝对定位,我们就找到第一行中.pin的最小高度,取得它的索引,这样就得到最矮的那个.pin,我们取得它的高度和索引,把第二行的这个.pin的top赋值第一行中.pin最小高度,left赋值第一行最矮的.pin的索引*.pin的宽度,这样第二行的第一个就在第一行的最矮的那个.pin下面且对齐。
最后我们把pinHarr中最小的那个高度换成最小高度加上新增的这个.pin高度(简单来说就是列高),此时最小高度变化了,我们下一次还是找最小高度放.pin,以此类推,每次放置.pin都是找最矮那一列放置。这样就实现了等宽不等高的对其布局了。
| 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22
 | function waterfall(parent,pin){         var aPin = parent.querySelectorAll(pin);         var iPiw = aPin[0].offsetWidth;         var num = Math.floor(document.documentElement.clientWidth/iPiw);         parent.style.cssText = 'width:'+num*iPiw+'px;margin:0 auto;';         var pinHarr = [];         aPin.forEach(function(el,index){            var pinH = el.offsetHeight;                if(index<num){                   pinHarr[index] = pinH;                  }else{                 var minH = Math.min.apply(null,pinHarr);                 var minindex =  pinHarr.findIndex(function(num){                                            return minH===num;                 });                 el.style.position = 'absolute';                 el.style.left = iPiw*minindex +'px';                 el.style.top  = minH+'px';                 pinHarr[minindex] = minH+el.offsetHeight;                }         });     }
 | 
接下来等页面生成的最后一个img加载出来后我们调用这个函数,布局就完成了。
| 1 2 3
 | oParent.getElementsByClassName('pin')[49].getElementsByTagName('img')[0].onload = function(){     waterfall(oParent,'.pin');             }
 | 
当页面滚动到底部时,我们还要让其生成新的dom,将其放在底部。
我们先写一个判断函数,判断滚动条滚动时到达底部没有,没有就返回false,有就返回true。
| 1 2 3 4 5 6 7
 | function isBottom(){     var aPin = oParent.querySelectorAll('.pin');     var lastPinh = aPin[aPin.length-1].offsetTop+Math.floor(aPin[aPin.length-1].offsetHeight/2);      var Sh = document.documentElement.scrollTop||document.body.scrollTop;     var Dh = document.documentElement.clientHeight;     return lastPinh<Sh+Dh?true:false; }
 | 
最后我们添加滚动事件,当到达底部时,我们遍历json,用createDom生成dom添加到#main底部,然后调用waterfall,让其让其一一队列排齐。
| 1 2 3 4 5 6 7 8
 | window.onscroll = function(){     if(isBottom()){         dataInt.forEach(function(val){             createDom(oParent,val.src);         });                      waterfall(oParent,'.pin');       } }
 | 
这样,一个等宽不等高的无限加载的瀑布流就实现了。效果如下:
本文代码地址:链接