浅谈响应式瀑布流的实现方式

61 篇文章 2 订阅
瀑布流主要应用在图片展示页面上。如果有一大批图片需要展示,原始图片尺寸不一致,又希望每张图片都能不剪裁,完整显示,那么就要给图片规定一个宽度,解放它们的高度。利用网页高度不限这个特性,充分利用页面的空间,尽可能的展示多的图片。瀑布流的实现方法挺多,但能做到响应式列数变化,自由布局的并不多。这次自己开发了一个作品集页面,正好研究一下响应式瀑布流的实现方法。

QQ截图20141114104135.png 


需要解决的问题:
1. 响应式多列:960px宽以上呈4列,750-960呈3列,400-750呈2列,400宽以下变成1列;
2. 由于需要做响应式,那么每块的宽度就不固定,导致高度更加不可能固定。后端输出的时候,图片(块)的高度本来就是未知的,要用JS实时取到它们的高度,以便调整布局。
3. 异步加载:页面拖到最低端的时候加载更多,这个很常见。


实现过程:
1. 先用offsetWidth取瀑布流大容器<ul>的实际宽度totalWidth,JS判断这个宽度可以放几列,但不给每列分配容器,而是用数组column记录每列的高度。给每个图片块分配一个<li>,每个<li>给一个class,4列就是col4,3列的就是col3。在css中给这些col+数字的class定宽度,比如这样:


  1. .col1 { width: calc(100% - 20px); }
  2. .col2 { width: calc(50% - 20px); }
  3. .col3 { width: calc(330% - 20px); }
  4. .col4 { width: calc(250% - 20px); }
复制代码


当然要做好兼容性,不兼容calc的浏览器(IE9-,安卓手机等)就用小一点的百分比代替;


2. 每个<li>的宽度定完后,高度都是auto,在JS里用offsetHeight取高度,用offsetWidth取宽度。然后设置每个<li>的style.top和style.left
style.top的值为数组column中最小的那个元素的值;
style.left的值为column中最小的那个元素的序号×每个<li>的offsetWidth宽度;
每个<li>的位置设置完毕后,重新把添加的高度存入数组column中,并再设置<ul>的高度比column中最大的那个元素再大一些


3. 先加载一定数量的<li>,监听页面滚动,滚动到底部再加载更多数量的,然后重新排列;


4. 监听页面resize事件,如果发生,则执行所有命令重新排列;


5. 给所有的JS执行一个延时,防止浏览器反复resize造成页面卡住;


6. 用CSS3的transition给每个<li>的位置加缓动效果,给<ul>的高度也加缓动效果,这样页面resize的时候会很舒服;


JS代码如下,版面限制,省略addEvent,addClass等几个常用的自定义函数的篇幅:



  1. var pinterest_doing = 0; //是否正在排列
  2. var pinterest_current = 0; //当前排列到第几个
  3. var pinterest_done = 0; //是否全部加载完毕
  4. var pinterestObj = document.getElementById("pinterestList"); //设定哪个<ul>容器作为瀑布流的总容器

  5. function pinterestInit(obj,add){
  6.         var perBlock = 16;//设定每次加载块数
  7.         var gapWidth = 25;//设定块间距
  8.         var containerPadding = 5;//设定外边距
  9.         var columns = 4;//设定最大列数

  10.         pinterest_doing = 1;//开始排列
  11.         obj.style.transition = "height 1s"; //给容器高度变化添加缓动
  12.         var totalWidth=obj.offsetWidth;
  13.         if(totalWidth<=720) { //根据容器宽度判断列数
  14.                 columns--; 
  15.                 if(totalWidth<=552) {
  16.                         columns--;
  17.                         if(totalWidth<=312) {
  18.                                 columns--;
  19.                         }
  20.                 }
  21.         }
  22.         obj.className="pinterestUl";
  23.         addClass(obj, "col"+columns);
  24.         var singleWidth=totalWidth/columns-gapWidth;
  25.         var column=new Array(); //建立一个存储每个列高度的数组
  26.         for(i=0;i<columns;i++){//set the columns and each top
  27.                 if (!column[i]) column[i]=0; //初始化数组内每个高度是0
  28.         }
  29.         function findMaxHeight(){ //查询数组内最高的高度
  30.                 var maxHeight=column[0];
  31.                 var maxColum=0;
  32.                 for(var i=0;i<column.length;i++){
  33.                         if(maxHeight<=column[i]){
  34.                                         maxHeight=column[i];
  35.                                         maxColum=i;
  36.                                 }
  37.                         }
  38.                 return {"maxHeight":maxHeight, "maxColum":maxColum } //输出最高高度的对象
  39.         }
  40.         function findMinHeight(){ //查询数组内最低高度
  41.                 var minHeight=column[0];
  42.                 var minColum=0;
  43.                 for(var i=0;i<column.length;i++){
  44.                         if(minHeight>column[i]){
  45.                                         minHeight=column[i];
  46.                                         minColum=i;
  47.                                 }
  48.                         }
  49.                 return {"minHeight":minHeight, "minColum":minColum } //输出最低高度对象
  50.         }

  51.         var totalItem=obj.children.length;
  52.         if(add) {
  53.                 pinterest_current+=perBlock; //判断是否需要增加加载量
  54.         }
  55.         for(var num=0; num<totalItem; num++ ){ //这里开始排列每块的位置
  56.                 if (num>= Math.max(pinterest_current, perBlock) ) break;
  57.                 obj.children[num].style.display="block";

  58.                 var atColum=findMinHeight().minColum;
  59.                 var atHeight=findMinHeight().minHeight;

  60.                 obj.children[num].style.left =  atColum * (singleWidth + gapWidth) + containerPadding + "px";
  61.                 obj.children[num].style.top = gapWidth + atHeight + "px" ;
  62.                 column[atColum] += obj.children[num].offsetHeight+gapWidth;

  63.         }
  64.         pinterest_current = num ; //记录下排列到第几个块
  65.         if(pinterest_current>=totalItem){//全部加载完毕
  66.                 pinterest_done=1;
  67.                 document.getElementById("pinterestDone").style.display="block";
  68.         }

  69.         obj.style.height= (findMaxHeight().maxHeight+30)+"px";
  70.         setTimeout( function(){ //过半秒再设置pinterest_doing为0,然后才能重新执行pinterestInit,防止浏览器崩溃
  71.                 pinterest_doing=0;
  72.         }, 500);
  73. }

  74. addEvent(window, "resize", function(){ //页面窗口尺寸变化监听
  75.         setTimeout( function(){
  76.                 if(pinterest_doing==0) { //pinterestInit执行的必要条件
  77.                         pinterest_doing=1;
  78.                         pinterestInit(pinterestObj);
  79.                 }
  80.         }, 500);
  81. });

  82. addEvent(window, "scroll", function(){ //滚动监听
  83.         if (document.body.scrollHeight-getViewPortSize().y <= getScrollOffsets().y+2){
  84.                 if(!pinterest_done){//如果没有全部加载完毕,显示loading图标
  85.                         addClass(pinterestObj ,"pinterestUl_loading");
  86.                 }else {//如果全部加载完毕,显示已经全部加载完毕的提示语
  87.                         document.getElementById("pinterestDone").style.display="block";
  88.                 }
  89.                 setTimeout( function(){
  90.                         if(pinterest_doing==0) {
  91.                                 pinterest_doing=1;
  92.                                 pinterestInit(pinterestObj, true );
  93.                         }
  94.                 }, 500);
  95.         }
  96. });
  97. addDOMLoadEvent( pinterestInit(pinterestObj) ); //页面DOM加载完毕才开始执行瀑布流排序
复制代码


CSS就不贴了,在线例子见http://blog.brain1981.com/artworks


转载请注明文章出处:http://blog.brain1981.com/829.html
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值