您当前的位置: 站长圈 > 技术文章 > 网页前端 > 浅谈响应式瀑布流的实现方式

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

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

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.  
  6. function pinterestInit(obj,add){
  7.         var perBlock = 16;//设定每次加载块数
  8.         var gapWidth = 25;//设定块间距
  9.         var containerPadding = 5;//设定外边距
  10.         var columns = 4;//设定最大列数
  11.  
  12.         pinterest_doing = 1;//开始排列
  13.         obj.style.transition = "height 1s"; //给容器高度变化添加缓动
  14.         var totalWidth=obj.offsetWidth;
  15.         if(totalWidth<=720) { //根据容器宽度判断列数
  16.                 columns--;
  17.                 if(totalWidth<=552) {
  18.                         columns--;
  19.                         if(totalWidth<=312) {
  20.                                 columns--;
  21.                         }
  22.                 }
  23.         }
  24.         obj.className="pinterestUl";
  25.         addClass(obj, "col"+columns);
  26.         var singleWidth=totalWidth/columns-gapWidth;
  27.         var column=new Array(); //建立一个存储每个列高度的数组
  28.         for(i=0;i<columns;i++){//set the columns and each top
  29.                 if (!column[i]) column[i]=0; //初始化数组内每个高度是0
  30.         }
  31.         function findMaxHeight(){ //查询数组内最高的高度
  32.                 var maxHeight=column[0];
  33.                 var maxColum=0;
  34.                 for(var i=0;i<column.length;i++){
  35.                         if(maxHeight<=column[i]){
  36.                                         maxHeight=column[i];
  37.                                         maxColum=i;
  38.                                 }
  39.                         }
  40.                 return {"maxHeight":maxHeight, "maxColum":maxColum } //输出最高高度的对象
  41.         }
  42.         function findMinHeight(){ //查询数组内最低高度
  43.                 var minHeight=column[0];
  44.                 var minColum=0;
  45.                 for(var i=0;i<column.length;i++){
  46.                         if(minHeight>column[i]){
  47.                                         minHeight=column[i];
  48.                                         minColum=i;
  49.                                 }
  50.                         }
  51.                 return {"minHeight":minHeight, "minColum":minColum } //输出最低高度对象
  52.         }
  53.  
  54.         var totalItem=obj.children.length;
  55.         if(add) {
  56.                 pinterest_current+=perBlock; //判断是否需要增加加载量
  57.         }
  58.         for(var num=0; num<totalItem; num++ ){ //这里开始排列每块的位置
  59.                 if (num>= Math.max(pinterest_current, perBlock) ) break;
  60.                 obj.children[num].style.display="block";
  61.  
  62.                 var atColum=findMinHeight().minColum;
  63.                 var atHeight=findMinHeight().minHeight;
  64.  
  65.                 obj.children[num].style.left =  atColum * (singleWidth + gapWidth) + containerPadding + "px";
  66.                 obj.children[num].style.top = gapWidth + atHeight + "px" ;
  67.                 column[atColum] += obj.children[num].offsetHeight+gapWidth;
  68.  
  69.         }
  70.         pinterest_current = num ; //记录下排列到第几个块
  71.         if(pinterest_current>=totalItem){//全部加载完毕
  72.                 pinterest_done=1;
  73.                 document.getElementById("pinterestDone").style.display="block";
  74.         }
  75.  
  76.         obj.style.height= (findMaxHeight().maxHeight+30)+"px";
  77.         setTimeout( function(){ //过半秒再设置pinterest_doing为0,然后才能重新执行pinterestInit,防止浏览器崩溃
  78.                 pinterest_doing=0;
  79.         }, 500);
  80. }
  81.  
  82. addEvent(window, "resize", function(){ //页面窗口尺寸变化监听
  83.         setTimeout( function(){
  84.                 if(pinterest_doing==0) { //pinterestInit执行的必要条件
  85.                         pinterest_doing=1;
  86.                         pinterestInit(pinterestObj);
  87.                 }
  88.         }, 500);
  89. });
  90.  
  91. addEvent(window, "scroll", function(){ //滚动监听
  92.         if (document.body.scrollHeight-getViewPortSize().y <= getScrollOffsets().y+2){
  93.                 if(!pinterest_done){//如果没有全部加载完毕,显示loading图标
  94.                         addClass(pinterestObj ,"pinterestUl_loading");
  95.                 }else {//如果全部加载完毕,显示已经全部加载完毕的提示语
  96.                         document.getElementById("pinterestDone").style.display="block";
  97.                 }
  98.                 setTimeout( function(){
  99.                         if(pinterest_doing==0) {
  100.                                 pinterest_doing=1;
  101.                                 pinterestInit(pinterestObj, true );
  102.                         }
  103.                 }, 500);
  104.         }
  105. });
  106. addDOMLoadEvent( pinterestInit(pinterestObj) ); //页面DOM加载完毕才开始执行瀑布流排序
复制代码


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


转载请注明文章出处:http://blog.brain1981.com/829.html