
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事件可能会绑定多个业务,所以本例中的直接覆盖绑定方式不宜直接拉入项目中去,要确保没有其他业务占用的情况下方可如此,否则可能会出现一些意外情况,比如无法执行、某些事件被覆盖等。
图片和文件放置在同一个目录,否则请修改相应的路径。当用鼠标怎么也滚不到底的时候,恭喜你已实现了经典的固定列宽瀑布流!