瀑布流主要应用在图片展示页面上。如果有一大批图片需要展示,原始图片尺寸不一致,又希望每张图片都能不剪裁,完整显示,那么就要给图片规定一个宽度,解放它们的高度。利用网页高度不限这个特性,充分利用页面的空间,尽可能的展示多的图片。瀑布流的实现方法挺多,但能做到响应式列数变化,自由布局的并不多。这次自己开发了一个作品集页面,正好研究一下响应式瀑布流的实现方法。
响应式瀑布流需要解决的问题:
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 2 3 4 | .col1 { width: calc(100% - 20px); } .col2 { width: calc(50% - 20px); } .col3 { width: calc(330% - 20px); } .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 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 | var pinterest_doing = 0; //是否正在排列 var pinterest_current = 0; //当前排列到第几个 var pinterest_done = 0; //是否全部加载完毕 var pinterestObj = document.getElementById("pinterestList"); //设定哪个<ul>容器作为瀑布流的总容器 function pinterestInit(obj,add){ var perBlock = 16;//设定每次加载块数 var gapWidth = 25;//设定块间距 var containerPadding = 5;//设定外边距 var columns = 4;//设定最大列数 pinterest_doing = 1;//开始排列 obj.style.transition = "height 1s"; //给容器高度变化添加缓动 var totalWidth=obj.offsetWidth; if(totalWidth<=720) { //根据容器宽度判断列数 columns--; if(totalWidth<=552) { columns--; if(totalWidth<=312) { columns--; } } } obj.className="pinterestUl"; addClass(obj, "col"+columns); var singleWidth=totalWidth/columns-gapWidth; var column=new Array(); //建立一个存储每个列高度的数组 for(i=0;i<columns;i++){//set the columns and each top if (!column[i]) column[i]=0; //初始化数组内每个高度是0 } function findMaxHeight(){ //查询数组内最高的高度 var maxHeight=column[0]; var maxColum=0; for(var i=0;i<column.length;i++){ if(maxHeight<=column[i]){ maxHeight=column[i]; maxColum=i; } } return {"maxHeight":maxHeight, "maxColum":maxColum } //输出最高高度的对象 } function findMinHeight(){ //查询数组内最低高度 var minHeight=column[0]; var minColum=0; for(var i=0;i<column.length;i++){ if(minHeight>column[i]){ minHeight=column[i]; minColum=i; } } return {"minHeight":minHeight, "minColum":minColum } //输出最低高度对象 } var totalItem=obj.children.length; if(add) { pinterest_current+=perBlock; //判断是否需要增加加载量 } for(var num=0; num<totalItem; num++ ){ //这里开始排列每块的位置 if (num>= Math.max(pinterest_current, perBlock) ) break; obj.children[num].style.display="block"; var atColum=findMinHeight().minColum; var atHeight=findMinHeight().minHeight; obj.children[num].style.left = atColum * (singleWidth + gapWidth) + containerPadding + "px"; obj.children[num].style.top = gapWidth + atHeight + "px" ; column[atColum] += obj.children[num].offsetHeight+gapWidth; } pinterest_current = num ; //记录下排列到第几个块 if(pinterest_current>=totalItem){//全部加载完毕 pinterest_done=1; document.getElementById("pinterestDone").style.display="block"; } obj.style.height= (findMaxHeight().maxHeight+30)+"px"; setTimeout( function(){ //过半秒再设置pinterest_doing为0,然后才能重新执行pinterestInit,防止浏览器崩溃 pinterest_doing=0; }, 500); } addEvent(window, "resize", function(){ //页面窗口尺寸变化监听 setTimeout( function(){ if(pinterest_doing==0) { //pinterestInit执行的必要条件 pinterest_doing=1; pinterestInit(pinterestObj); } }, 500); }); addEvent(window, "scroll", function(){ //滚动监听 if (document.body.scrollHeight-getViewPortSize().y <= getScrollOffsets().y+2){ if(!pinterest_done){//如果没有全部加载完毕,显示loading图标 addClass(pinterestObj ,"pinterestUl_loading"); }else {//如果全部加载完毕,显示已经全部加载完毕的提示语 document.getElementById("pinterestDone").style.display="block"; } setTimeout( function(){ if(pinterest_doing==0) { pinterest_doing=1; pinterestInit(pinterestObj, true ); } }, 500); } }); addDOMLoadEvent( pinterestInit(pinterestObj) ); //页面DOM加载完毕才开始执行瀑布流排序 |
CSS就不贴了,在线例子:
https://blog.brain1981.com/artworks
https://www.becomingjenny.net
本站所有文章均为原创,欢迎转载,请注明文章出处:https://blog.brain1981.com/829.html。百度和各类采集站皆不可信,搜索请谨慎鉴别。技术类文章一般都有时效性,本人习惯不定期对自己的博文进行修正和更新,因此请访问出处以查看本文的最新版本。
您也可以扫描左边的二维码,关注我们的微信公众号,在微信上查看我们的案例。