网站备案终于下来了,等了十几天,不容易呀,顺便搞了个免费的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'); } }
|
这样,一个等宽不等高的无限加载的瀑布流就实现了。效果如下:
本文代码地址:链接