收藏本站腾讯微博新浪微博

经典论坛

 找回密码
 注册

QQ登录

只需一步,快速开始

蓝色理想 最新研发动态 网站开通淘帖功能 - 蓝色理想插件 论坛内容导读一页看论坛 - 给官方提建议

论坛活动及任务 地图和邮件任务 请多用悬赏提问 热夏来袭,选一款蓝色理想的个性T恤吧!

手机上论坛,使用APP获得更好体验 急需前端攻城狮,获得内部推荐机会 论坛开通淘帖功能,收藏终于可以分类了!

搜索
查看: 6599|回复: 13

瀑布布局的JavaScript实现方式

  [复制链接]
发表于 2012-3-12 11:42:55 | 显示全部楼层 |阅读模式
本帖最后由 xiaobaov2 于 2012-3-12 11:56 编辑

瀑布式布局是一种多列等宽不等高的一种页面展示方式,用于图片来源比较复杂,图片尺寸比较复杂时可以使用的一种展示方式,这种展示方式比较优美,让人有种错落有致的感觉.这种展示方式在淘宝的我要买,新浪微博的广场以及蘑菇街等等网站都有应用.这里是我刚刚做的一个小站91美图网

91.png

实现布局有三个思路:

最传统的思路,多弄几个容器,分几列,然后往每个列里面插入元素.其实用table分几列实现更加方便:P;
使用html5中css3的多列布局来实现.参见w3c标准中的css3多列布局模块;
使用绝对布局,通过javascript生成元素的布局位置.
前两种方法在网上都有比较详细的介绍,我这里就不再多说了,这里主要说一下我做的第三种实现的优缺点以及我的实现思路.

第三种方案是所有的要布局的元素都是绝对定位的,然后通过javascript来判断每个元素位置,动态设置位置实现布局.

缺点

需要使用javascript来遍历元素,然后要根据前一个元素来判断后一个元素的位置,这样可能对一些老版本的浏览器造成负担,特别是IE6这种老古董,而且在javascript失效的时候,整个页面的布局都会乱掉.另外如果整个页面宽度是变化的,则可能每次窗口尺寸改变时都要重新计算所有元素的位置,在页面中元素较多的时候可能会有闪烁的现象.另外如果页面中出现图片,则需要实现定义好图片的尺寸,否则会出现无法正确计算元素位置的情况.

优点

布局更加灵活,元素绝对定位,可以使用javascript灵活操作.页面宽度改变时可以重新布局整个页面.可以使页面的中的元素真正流动起来,让新添加的元素插入到高度最低的列,使页面的低端更加整齐,对插入的元素高地要求较低.可以较为方便的实现延迟加载.

具体实现

最开始我实现的时候是通过使用javascript生成虚拟的列,根据元素的顺序为每个元素分配一个列和行,然后计算每个元素的位置,举个例子,现在假设有四个列:

使其在页面中布局.事实上这个解决办法跟第一种和第二种是一个道理的.最后页面每列的高度差别可能会很大.
  1. //getElements()方法用于获取页面中的元素
  2. var items = getElements();
  3. var columnCount = 4;
  4. var columnWidth = 230;
  5. var padding = 8;
  6. //遍历所有元素
  7. for(var i = 0, len = items.length; i < len; i++){
  8.         //获取当前的元素
  9.         var currentItem = items[i];
  10.         //获取当前对象的列
  11.         var currentColumn = (i + 1) % 4;
  12.         //获取当前对象的行
  13.         var currentLevel = parseInt(i / 4);

  14.         //有了当前的行跟列可以根据上一层的对象计算出当前对象的高度
  15.         var left = currentColumn * columnWidth;
  16.         var top = items[i - 4] ? 0 : items[i - 4].style.top + items[i - 4].clientHeight + padding;
  17.         //设置当前的位置
  18.         currentItem.style.top = top + 'px';
  19.         currentItem.style.left = left + 'px';
  20. }
复制代码
代码和逻辑都比较简单,根据当前的行跟列计算出位置就行了.但是这个逻辑还是会出现元素高地差距过大的情况.看一下新浪weibo的广场图片效果:


新浪weibo广超图片列表效果图



可以看到越到最后可能列高度之间的差距会越大.这不是我们想要实现的效果.

所以我这里换了一个思路,虚拟的列还是要有的,但是行的概念我们抛弃掉,我采用的是一个类似流动的概念,新插入的元素是根据每个列的高度,那个高度最低就流向哪个列,最后确保每个列的高度都趋近一致,实现我们想要的效果.当然我们可以采取获取所有元素的高度,然后统一计算一下,获取一个最佳的排列方法,但是这会给浏览器带来较大的计算量,而且如果不断的加载更多的图片我们会得不偿失,所以我们采用的是一个流动的模型,只让当前对象寻找最低的高度然后插入.

这里我实现了一个Column对象,一个ImgItem对象.Column对象用于维护每一列的信息,包括列的最到高度列宽度等列信息.ImgItem对象保存了一个html节点对象,还有一些设置元素位置,获取当前元素位置的方法.

下面是Column对象的代码:
  1. var Column = function(order){
  2.         this.order = order;
  3.         this.maxHeight = 0;
  4.         this.columnWidth = 230;
  5.         this.left = this.columnWidth * order;
  6.         this.lastItem = null;
  7.         this.positioned = false;
  8.         this.setReferItem = function(item){
  9.                 this.lastItem = item;
  10.         }

  11.         this.getHeight = function(){
  12.                 if(this.lastItem){
  13.                         this.maxHeight = this.lastItem.getBottom();
  14.                 }
  15.                 return this.maxHeight;
  16.         }

  17.         this.getLeft = function(){
  18.                 return this.left;
  19.         }
  20. };
  21. ImgItem对象的代码:

  22. var ImgItem = function(referNode, column){
  23.         this.referNode = referNode;
  24.         this.bottom = -1;
  25.         this.positioned = false;
  26. };

  27. ImgItem.prototype = {
  28.         /*
  29.          *set the refer node's top
  30.          * @param value: Number
  31.          */
  32.         setTop: function(value){
  33.                 this.referNode.style.top = value + 'px';
  34.         },
  35.         /*
  36.          *set the refer node's left
  37.          * @param value: Number
  38.          */
  39.         setLeft: function(value){
  40.                 this.referNode.style.left = value + 'px';
  41.         },
  42.         /*
  43.          *get the refer node bottom position
  44.          */
  45.         getBottom: function(){
  46.                 if(this.positioned){
  47.                         if(this.bottom < 0){
  48.                                 this.bottom = parseInt(this.referNode.style.top) + this.referNode.clientHeight;
  49.                         }
  50.                         return this.bottom;
  51.                 }else{
  52.                         throw("current node has not been positioned!");
  53.                 }
  54.         },
  55.         setPosition: function(column){
  56.                 this.positioned = true;
  57.                 this.setLeft(column.getLeft());
  58.                 this.setTop(column.getHeight() + 10);
  59.                 column.setReferItem(this);
  60.         }
  61. };
复制代码
基础打好了,下面要做的就是给元素进行布局了:
  1. //首先根据配置信息中的列数初始化列
  2. for(var i = 0, len = this.config.columnCount; i < len; i++){
  3.         this.columns.push(new Column(i));
  4. }
  5. //获取页面上已存在的对象
  6. var liItems = document.getElementById('img_list').getElementsByTagName('li');
  7. //将所有的对象进行布局
  8. for(i = 0, len = liItems.length; i < len; i++){
  9.         this.addNewItem(liItems[i]);
  10. }
  11. 好吧这里还用到了一个addNewItem方法:

  12. getMinHeightColumn: function(){
  13.         var minHeight = -1, tempColumn = null;
  14.         //遍历所有的列,获取当前高度最低的列,并返回
  15.         for(var i = 0,len = this.columns.length; i < len; i++){                 if(minHeight > this.columns[i].getHeight() || minHeight == -1){
  16.                         minHeight = this.columns[i].getHeight();
  17.                         tempColumn = this.columns[i];
  18.                 }
  19.         }

  20.         return tempColumn;
  21. },

  22. getMaxHeight: function(){
  23.         var maxHeight = -1;
  24.         //遍历列对象,获取高度最高的列并返回高度
  25.         for(var i = 0, len = this.columns.length; i < len; i++){
  26.                 if(maxHeight < this.columns[i].getHeight()){
  27.                         maxHeight = this.columns[i].getHeight();
  28.                 }
  29.         }

  30.         return maxHeight;
  31. },
  32. addNewItem: function(liItem){
  33.         //设置元素的位置
  34.         var imgItem = new ImgItem(liItem);
  35.         imgItem.setPosition(this.getMinHeightColumn());
  36.         this.cachedItems.push();
  37.         //设置容器的高度
  38.         document.getElementById('img_list').style.height = this.getMaxHeight() + 'px';
  39. }
复制代码
基本的布局逻辑已经都齐了,还有的就是lazyload的一些逻辑了,这些逻辑都比较通用.加载后布局对象的逻辑是相同的.这里就不再赘述了.

点击下载源文件

抛砖引玉,如有谬误还请不吝指教.

本帖被以下淘专辑推荐:

发表于 2012-4-19 09:53:16 | 显示全部楼层
不错,不顶对不起楼主。
回复 支持 反对

使用道具 举报

发表于 2012-4-26 11:55:44 | 显示全部楼层
不错。学习学习
回复 支持 反对

使用道具 举报

发表于 2012-4-26 15:19:39 | 显示全部楼层
提示: 作者被禁止或删除。
回复 支持 反对

使用道具 举报

发表于 2012-4-26 19:30:17 | 显示全部楼层
谢谢楼主啊,真是太棒了
回复 支持 反对

使用道具 举报

发表于 2012-5-2 10:28:49 | 显示全部楼层
赞,就是不好意思打开你的站
回复 支持 反对

使用道具 举报

发表于 2012-5-23 13:48:43 | 显示全部楼层
不错,支持下
回复 支持 反对

使用道具 举报

发表于 2012-5-23 21:02:09 | 显示全部楼层
提示: 作者被禁止或删除。
回复 支持 反对

使用道具 举报

发表于 2012-6-13 09:37:08 | 显示全部楼层
比较笨 看不懂  很好奇怎么实现的
回复 支持 反对

使用道具 举报

发表于 2012-6-27 15:49:09 | 显示全部楼层
很详尽的讲解啊,那些镁铝的图片让我遭遇了同事的围观
回复 支持 反对

使用道具 举报

发表于 2012-7-18 09:50:03 | 显示全部楼层
不错,学习了,高手!!!
回复 支持 反对

使用道具 举报

发表于 2012-7-18 12:15:48 | 显示全部楼层
现在好多网站都喜欢用这个形式来展示图片的。
回复 支持 反对

使用道具 举报

发表于 2012-7-18 13:21:27 | 显示全部楼层
图片展示站 瀑布流 很帅的。
回复 支持 反对

使用道具 举报

发表于 2012-7-18 13:36:30 | 显示全部楼层
楼主说的不错,学习了
回复 支持 反对

使用道具 举报

您需要登录后才可以回帖 登录 | 注册

本版积分规则

QQ|小黑屋|Archiver|手机版|blueidea.com ( 湘ICP备12001430号 )  

GMT+8, 2020-7-15 00:51 , Processed in 0.124684 second(s), 11 queries , Gzip On, Memcache On.

Powered by Discuz! X3.2 Licensed

© 2001-2013 Comsenz Inc.

快速回复 返回顶部 返回列表