JavaScript实战-JavaScript、jQuery、HTML5、Node.js实例大全(第2版)
上QQ阅读APP看书,第一时间看更新

5.2 固定列宽的简单瀑布流实现

虽然瀑布流有好有坏,但是这个极具创意的网页交互设计,其核心技术值得去探索一番,瀑布流经过一段时间的改良出现了不同款式,但基本来说分为固定和不固定的,很简单,固定的尺寸在程序处理和维护方面相对比较容易,而不固定的则要兼容许多东西,先来看看一个简单的固定列宽瀑布流如何实现。

5.2.1 简单的HTML结构

相信很多人都会去逛淘宝,淘宝有很多子站都采用过瀑布流。通过图5-4来看下“淘宝哇哦”的瀑布流结构。

图5-4 “淘宝哇哦”的固定列宽瀑布流布局

“哇哦”采用的是固定列宽模式,共分为4列,以result-col为class名,外层套了一个class名为result-box的div控制整个瀑布居中显示,并且限制宽度,不会因为窗口的大小变化而变化。

笔者也以此为思路,构造了【范例5-1】这样的瀑布流HTML结构。

【范例5-1 简单的HTML结构】

    1.  <! DOCTYPE html>
    2.  <html>
    3.    <head>
    4.     <title>简单固定列宽瀑布流</title>
    5.     <link rel="stylesheet" href="eg5.css" />
    6.    </head>
    7.    <body>
    8.      <div class="main">
    9.               <div class="col"><img src="1.jpg" alt="" /><p>[1.jpg]</p></div>
    10.             <div class="col"><img src="2.jpg" alt="" /><p>[2.jpg]</p></div>
    11.             <div class="col"><img src="3.jpg" alt="" /><p>[3.jpg]</p></div>
    12.             <div class="col"><img src="4.jpg" alt="" /><p>[4.jpg]</p></div>
    13.     </div>
    14.     <script src="base.js"></script>
    15.     <script src="eg5.js"></script>
    16.  </body>
    17. </html>

根据实际情况,通常后端会生成第一批默认数据,而不是让JavaScript来构造,所以eg5.css是一个简要的样式文件,body里只留了一个class名为main的div,第一批数据就是每列先显示第一行数据,base.js通过前面几章节积累下来的公共代码放置其中,eg5.js是本章范例必要的代码。

5.2.2 让瀑布流动起来

打好基建之后,就需要编写JavaScript代码了。首先如果数据不够显示一屏,就用新数据来补足它,在补充的时候是根据4列中最矮的那一个为优先补充,因为高矮尺寸一般只有在客户端才看得到,服务端虽然也可以计算,但是会浪费资源,客户端的内存和CPU能用则多用,只要不让客户端变慢就行。

只要图片高度不一致,通过这样的思路很快就可以看到一个“瀑布流”,这仅仅是静态的,一般滚动的时候瀑布流都会添加数据,所以接下来就是添加滚动事件,只要有滚动就计算,然后补充数据。

先看图5-5的效果。

图5-5 固定列宽瀑布流

在网上收集了一些固定宽度不固定高度的图片,简单设置了一下页面的样式,在实际项目中,外观样式设置可能更复杂一些,本书重点研讨JavaScript,所以还是先看看其实现代码,如【范例5-2】所示。

【范例5-2 固定列宽瀑布流实现】

    1.  eg.getDataList = function(min, max){
          //模拟构造数据,实际上这些数据由后端提供
    2.      var lst = [], n=8;    //保存数据
    3.      for(var i=0; i<n; i++){                               //每次模拟n 条
    4.              var k = min + parseInt(Math.random()*(max-min)); //随机指定范围的数
    5.              lst.push(k+".jpg"); //拼接成字符串
    6.      }
    7.      return lst; //返回数组
    8.  };
    9.  eg.cols = eg.getElementsByClassName("col"); //把目标对象缓存起来
    10. eg.colh = [0,0,0,0];                    //存取每列的高度
    11. eg.getColMin = function(){            //计算4 列高度
    12.     var min = 0, m = {};
    13.     for(var i=0; i<4; i++){
    14.             min = parseInt(eg.cols[i].offsetHeight);
    15.             eg.colh[i] = min;
    16.             m[min] = i;
    17.     }
    18.    return eg.cols[m[Math.min.apply(Array, eg.colh)]||0]; //返回最小高度的对象
    19. }
    20. eg.add = function(dl){ //追加数据的方法
    21.     for(var i in dl){
    22.             var newDiv = document.createElement("div")
    23.             var newImg = document.createElement("img");
    24.                      newImg.src = dl[i];
    25.                      newDiv.appendChild(newImg);
    26.                      newDiv.innerHTML += '<p>['+dl[i]+']</p>';
    27.                     eg.getColMin().appendChild(newDiv); //追加到最小高度列里
    28.     }
    29. };
    30. eg.scroll = function(){//滚动条事件处理
    31.    window.onscroll = function(){//onscroll, onload, onresize 只能这样添加
    32.             var doc = document;
    33.             var  top  =  doc.documentElement.scrollTop  ||  doc.body.scrollTop;
                  //滚动条到顶部的高度
    34.             var winH=doc.documentElement.clientHeight||doc.body.clientHeight;
                  //可视窗口的高度
    35.             if(Math.min.apply(Array, eg.colh) < top+winH){
                  //如果最小高度小于可视区域,就补充
    36.                     eg.add(eg.getDataList(1,35)); //随机获取数据,并追加到最后
    37.             }
    38.     }
    39. }

上面代码中的eg.getElementsByClassName()方法是之前定义过的一个方法,存放在base.js文件中,通过【范例5-1】可知,默认数据很少,需要在初始化的时候补充一些,这就要在HTML页面增加一个script标签,先调用eg.getColMin()获取已经存在的数据高度并保存到eg.colh数组中以便后面判断使用,然后调用eg.getDataList()方法模拟一批数据,正规项目中会用AJAX去服务端请求,然后把数据用eg.add()方法追加到后面。最后还要调用eg.scroll()方法绑定滚动条事件的监听,加入代码是这样的:

        <script>
            eg.getColMin();                      //计算第一批数据的高度
            var dl = eg.getDataList(5,35);    //初始化一些数据
            eg.add(dl);                           //补充剩下的数据
            eg.scroll();                          //启动滚动条监听
        </script>

技巧提示

由于真实项目中,window.onscroll事件可能会绑定多个业务,所以本例中的直接覆盖绑定方式不宜直接拉入项目中去,要确保没有其他业务占用的情况下方可如此,否则可能会出现一些意外情况,比如无法执行、某些事件被覆盖等。

图片和文件放置在同一个目录,否则请修改相应的路径。当用鼠标怎么也滚不到底的时候,恭喜你已实现了经典的固定列宽瀑布流!