导读:瀑布流,又称瀑布流式布局。是比较流行的一种网站页面布局,视觉表现为良莠不齐的多栏布局,随着页面滚动条向下滚动,这种布局还会不断加载数据块并附加至当前尾部。最先采用此布局的网站是Pinterest,逐渐在国内流行开来。国内大多数清新站基本为这类风格,像花瓣网、蘑菇街、美丽说等。html
改进版的代码见:github,能够与现有的进行对比,见文章末尾。git
最近在好多地方看到瀑布流的字眼,感受真的很不错,因而就想本身能不能写一个呢,并且是响应式的。通过将近两天的研究,终于写出来了,先传几张图给你们看看最终的效果:github
随着浏览器页面的大小调整。布局从四列逐渐变成三列两列,甚至是一列(图像的块的宽度是保持不变的)。数组
在我看来,在复杂的程序其实都是由很简单的函数组合在一块儿的,另外有一些常常用到的部分也会被抽取出来当作一个新的函数。而后,为了封装,会将一些变量也封装起来,而后经过外部传递进来,最后就成了咱们如今看到的。其实一开始,不多人可以肯定要怎么写,且能肯定须要传进哪些参数。一开始只能大概肯定须要作哪些,须要哪些函数,写着写着,发现缺了在添加就行了。至少我本身是这样的(也许是我如今太渣了)。浏览器
好了,接下来讲一说 具体实现的过程。请结合源码来看。微信
第一步:咱们须要建立这些图像块,也就是源码中的createDiv函数。这里为了节省性能,咱们采用字符串的形式,而不用createElement等这些函数。而后这里你会发现一个问题,咱们生成的块的大小是同样的。这时候,咱们就须要用到random函数了。经过该函数来生成高度不同的图像块。dom
第二步:怎样肯定图像的列数和怎样让他随着页面的改变而改变本身的列数。这里咱们就得用到widthchange函数。首先图像块的父容器的外边距是20px;所以总的宽度就得减去40(这里以浏览器页面来算)。而后再根据图像块的宽度,来肯定每一列之间的列数和间距。ide
第三步:设置样式setStyle。图像块的大小肯定以后,咱们得设置图像块的样式,好比大小,间距等等。函数
第四步:把上面的结合起来,进行封装就行了。布局
-------------------------------------------------------------------------------------------------------------------
可是呢,咱们得考虑一个问题,就是图片太多了,咱们不可能一次让全部图片都下载,这样会影响网页的性能和用户的体验。那么有没有什么好的办法呢?咱们只让可视区的图片显示就行了。那怎么实现呢,咱们来分析下
一、这里涉及到上面的createDiv函数,得先说清楚。咱们得将img的src的地址设为其余地址或为空。而后再自定义一个data-src属性,属性值为图片的地址。
二、可视区的图片显示。也就是咱们得先判断图片的位置是否是在这个可视区内,这里就得用到pageY,show函数,这些函数会用到offsetTop,scrollTop和clientHeight等这些属性。
三、若是在的话,就将data-src的值赋给src,而且将其从数组中去掉,表示该图片已经被加载。
四、最后将函数进行修改,封装。再绑定好各个事件就大功告成了。
最后附上源码供你们研究,若是以为好的话,能够加个人我的微信公众号,这样你就不用访问博客就能够获取相关信息。
公众号名字:JavaScript学习与实践
微信号:learnpracticeJs
公众号二维码:
欢迎你们关注个人公众号,你的关注就是我最大的支持。
源代码:
<!DOCTYPE html> <html> <head> <meta http-equiv="Content-Type" content="text/html; charset=utf-8" /> <title>瀑布流 by huansky</title> <style> #container{ position: relative; top:20px; margin: 20px 20px; background: #c00; } #container .wf{ position: absolute; border: 1px solid #ccc; font-size: 40px; border-radius: 10px; overflow: hidden; } p{ margin: 0px; padding: 0px; } h1{ text-align: center; } </style> </head> <body> <h1>瀑布流 by huansky</h1> <div id="container">54454</div> </body> <script > function wf(obj){ this.colHeight=[]; //每一列的高 this.colLeft=[]; //每一列的位置; 由于列是等宽的,因此每一列的位置也是固定的。 this.flag=0; //标志位,包含图片的容器只须要建立一次 this.imgWidth=obj.wth; //设置每一列的宽度 this.id=document.getElementById(obj.id); //获取最外层的容器 this.classname=document.getElementsByClassName(obj.classname); //获取瀑布流的类名 //变量必定要在init()以前给定义,不能放在init()以后 this.init(); //初始化 } wf.prototype={ //获取m到n的随机值 random:function(m,n){ return Math.ceil(Math.random()*(n-m)+m); }, //最小值的下标 getMinCol:function(arr){ var ca = arr,cl = arr.length,temp = ca[0],minc = 0; for(var ci = 0; ci < cl; ci++){ if(temp > ca[ci]){ temp = ca[ci]; minc = ci; } } return minc; }, //获取页面的大小,从而调整列数。 widthchange:function(){ winWidth = document.body.clientWidth; //获取页面的宽度 //console.log("ttt "+document.documentElement.clientHeight); var cols=Math.floor((winWidth-40)/this.imgWidth); //40是页面的两边的边距 //console.log(this.imgWidth); var colsmar=Math.floor((winWidth-40-20*(cols+1))/this.imgWidth); //列数 //console.log(colsmar); var cmargin=Math.floor(winWidth-this.imgWidth*colsmar-40)/(colsmar+1); //每一列的间距 //console.log(cmargin); for(var i=0;i<colsmar;i++){ this.colHeight[i]=20; this.colLeft[i]=(i+1)*cmargin+i*this.imgWidth; } }, //建立页面视图 createDiv:function(m){ var div=""; for(var n=0; n<m;n++){ var height=this.random(150,350); div+="<div class='wf' style=height:"+height+"px;width:"+this.imgWidth+"px;><p>"+(n+1)+"</p><img src='img/loading.gif' data-src='img/"+(n+1)+".png' style=height:"+(height-45)+"px;width:"+this.imgWidth+"px;></div>"; } this.id.innerHTML=div; }, //设置每一个小块的样式,边距等等 setStyle:function(){ for(var i=0;i<50;i++){ var lowcol=this.getMinCol(this.colHeight); this.classname[i].style.left=this.colLeft[lowcol]+"px"; this.classname[i].style.top=this.colHeight[lowcol]+"px"; this.colHeight[lowcol]=this.classname[i].offsetHeight+this.classname[i].offsetTop+20;//offsetTop是数值 } }, init:function(){ if(!this.flag){ //只建立小块一次,否则会每次都得从新加载图片 this.createDiv(50); this.flag=1;//设置flag=1,这样下次就不会加载了 } this.widthchange(); //newContainer.init(); this.setStyle(); } } //只加载在可视区内的图片 function LazyLoad(id){ this.container=document.getElementById(id); //获取id this.imgs=this.getImgs(); //获得img数组 this.init(); } LazyLoad.prototype={ init:function(){ this.update(); }, getImgs:function(){ var arr=[]; var imgs=this.container.getElementsByTagName("img"); for (var i=0,len=imgs.length;i<len;i++){ arr.push(imgs[i]); } return arr; }, update:function(){ if(!this.imgs.length){ //console.log(this.imgs.length); return; } var i=this.imgs.length; for(--i;i>=0;i--){ if (this.show(i)){ this.imgs[i].src=this.imgs[i].dataset.src; this.imgs.splice(i,1); //console.log(newContainer.imgs[i].dataset.src); } } }, //显示图片,断定图片是否在但是区域内。 show:function(i){ var img=this.imgs[i], scrollTop=document.documentElement.scrollTop||document.body.scrollTop, scrollBottom=scrollTop+document.documentElement.clientHeight, imgTop=this.pageY(img), imgBottom=imgTop+img.offsetHeight; //若是知足,让他显示。 if(imgBottom>scrollTop && imgBottom<scrollBottom || (imgTop>scrollTop && imgTop<imgBottom)) return true; return false; }, //获取图片的最高点的y坐标 pageY:function(element){ if(element.offsetParent){ return element.offsetTop+this.pageY(element.offsetParent); }else{ return element.offsetTop; } } } //var dom=document.getElementById("container"); //var cName=document.getElementsByClassName("wf"); //绑定事件函数 function on(element,eventName,listener){ if (element.addEventListener){ element.addEventListener(eventName,listener,false); } else if (element.attachEvent){ element.attachEvent('on'+eventName,listener); } else element['on'+eventName]=listener; } var newWf=new wf({wth:300,id:"container",classname:"wf"});//建立瀑布流 var newContainer=new LazyLoad("container"); //惰性加载函数 on(window,"resize",function(){ newWf.init(); newContainer.update(); }) //绑定scroll事件 on(window,"scroll",function(){ newContainer.update(); }) </script> </html>