瀑布流小实例

网站备案终于下来了,等了十几天,不容易呀,顺便搞了个免费的https证书,把评论也换成畅言的了,来必力太挫了,ui看见想打人系列。

瀑布流,又称瀑布流式布局。其特点是随着页面滚动条向下滚动,不断加载数据块并附加至当前尾部。从Pinterest开始兴起,国内也有许多网站采用这种样式,不过大多数为了节约代码采用的是等宽等高的样式。今天我要演示的是等宽不等高如何进行瀑布流加载。

html结构

由于我们这里瀑布流的布局是采用绝对定位的,所以html结构非常简单,加载的html都是通过js操控的。

<main>
    <div id="main">
         <div class="pin">       //js生成的
             <div class="box">
                  <img src="./images/0.jpg">
              </div>
         </div>
    </div>
</main>

css

css没什么好说的,也是非常简单,由于其等宽不等高的性质,我们把.box的宽度都定死,这里由于要整体居中,由于.pinpadding-left:14px;,故而而把#main设置了left: -7px;让其整体居中一下。

*{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。
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节点。
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都是找最矮那一列放置。这样就实现了等宽不等高的对其布局了。

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加载出来后我们调用这个函数,布局就完成了。

oParent.getElementsByClassName('pin')[49].getElementsByTagName('img')[0].onload = function(){
    waterfall(oParent,'.pin');
}
当页面滚动到底部时,我们还要让其生成新的dom,将其放在底部。

我们先写一个判断函数,判断滚动条滚动时到达底部没有,没有就返回false,有就返回true。

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,让其让其一一队列排齐。

window.onscroll = function(){
    if(isBottom()){
        dataInt.forEach(function(val){
            createDom(oParent,val.src);
        });
        waterfall(oParent,'.pin');
    }
}

这样,一个等宽不等高的无限加载的瀑布流就实现了。效果如下:

本文示例:链接
本文代码地址:链接

您可能还喜欢...

发表回复

您的电子邮箱地址不会被公开。 必填项已用 * 标注