html部分
<!DOCTYPE html>
<html>
<head>
<title>瀑布流实现</title>
<link rel="stylesheet" type="text/css" href="css/waterfall.css">
</head>
<body>
<div class="warp" id="warp">
<div class="box">
<img src="images/1.png">
<a href="">第一怪-竹筒当烟袋</a>
</div>
<div class="box">
<img src="images/2.png">
<a href="">第二怪-草帽当锅盖</a>
</div>
<div class="box">
<img src="images/3.png">
<a href="">第一怪-竹筒当烟袋</a>
</div>
<div class="box">
<img src="images/4.png">
<a href="">第一怪-竹筒当烟袋</a>
</div>
<div class="box">
<img src="images/5.png">
<a href="">第一怪-竹筒当烟袋</a>
</div>
<div class="box">
<img src="images/6.png">
<a href="">第一怪-竹筒当烟袋</a>
</div>
<div class="box">
<img src="images/7.png">
<a href="">第一怪-竹筒当烟袋</a>
</div>
<div class="box">
<img src="images/8.png">
<a href="">第一怪-竹筒当烟袋</a>
</div>
<div class="box">
<img src="images/9.png">
<a href="">第一怪-竹筒当烟袋</a>
</div>
<div class="box">
<img src="images/10.png">
<a href="">第一怪-竹筒当烟袋</a>
</div>
<div class="box">
<img src="images/11.png">
<a href="">第一怪-竹筒当烟袋</a>
</div>
<div class="box">
<img src="images/12.png">
<a href="">第一怪-竹筒当烟袋</a>
</div>
<div class="box">
<img src="images/13.png">
<a href="">第一怪-竹筒当烟袋</a>
</div>
<div class="box">
<img src="images/14.png">
<a href="">第一怪-竹筒当烟袋</a>
</div>
<div class="box">
<img src="images/15.png">
<a href="">第一怪-竹筒当烟袋</a>
</div>
<div class="box">
<img src="images/16.png">
<a href="">第一怪-竹筒当烟袋</a>
</div>
<div class="box">
<img src="images/17.png">
<a href="">第一怪-竹筒当烟袋</a>
</div>
<div class="box">
<img src="images/18.png">
<a href="">第一怪-竹筒当烟袋</a>
</div>
</div>
<script type="text/javascript" src="js/waterfall.js"></script>
<script type="text/javascript" src="../vendor/jquery-1.12.4.min.js"></script>
</body>
</html>
css部分
/All Tags/
*{
margin:0;
padding:0;
border:none;
}
body{
background:#ddd;
}
img{
border: none;
}
a{
text-decoration: none;
color:#444;
}
a:hover{
color: #999;
}
.warp{
position: relative;
width: auto;
height:auto;
margin:0 auto;
}
.warp > div{
float: left;
box-sizing: border-box;
width:280px;
height: auto;
margin:10px;
padding:10px;
border-radius:5px;
background:#fff;
}
.warp >div >img{
width:100%;
}
.warp >div >a{
display: block;
font-size:18px;
font-weight:bold;
line-height: 40px;
text-align:center;
}
js部分
//js实现瀑布流
/*思路:1、找到上一行最小高度的值和最小高度所在索引
2、对后面的盒子设置绝对定位,通过设置top和left值布局
如何找到最小高度?
将平铺的盒子高度一个个放进一个数组,使用max.min.apply(null,arr[])方法将上一行最小高度值找到
如何找到最小高度所在索引?
for in循环遍历高度数组,将最小值高度赋值给arr[index],返回index
知识点:
scrollHeight: 获取对象的滚动高度。
scrollLeft:设置或获取位于对象左边界和窗口中目前可见内容的最左端之间的距离
scrollTop:设置或获取位于对象最顶端和窗口中可见内容的最顶端之间的距离
scrollWidth:获取对象的滚动宽度
offsetHeight:获取对象相对于版面或由父坐标 offsetParent 属性指定的父坐标的高度
offsetLeft:获取对象相对于版面或由 offsetParent 属性指定的父坐标的计算左侧位置
offsetTop:获取对象相对于版面或由 offsetTop 属性指定的父坐标的计算顶端位置
clientX 设置或获取鼠标指针位置相对于当前窗口的 x 坐标,其中客户区域不包括窗口自身的控件和滚动条。
clientY 设置或获取鼠标指针位置相对于当前窗口的 y 坐标,其中客户区域不包括窗口自身的控件和滚动条。
offsetX 设置或获取鼠标指针位置相对于触发事件的对象的 x 坐标。
offsetY 设置或获取鼠标指针位置相对于触发事件的对象的 y 坐标。
screenX 设置或获取获取鼠标指针位置相对于用户屏幕的 x 坐标。
screenY 设置或获取鼠标指针位置相对于用户屏幕的 y 坐标。
x 设置或获取鼠标指针位置相对于父文档的 x 像素坐标(亦即相对于当前窗口)。
apply()方法分析:简单的说,通过apply(null,arr)方法,将一个数组转化为一行参数。
*/
//模拟数据,json格式
var data=[
{
"src":"1.png",
"title":"我是十八怪"
},
{
"src":"2.png",
"title":"我是十八怪"
},
{
"src":"3.png",
"title":"我是十八怪"
},
{
"src":"4.png",
"title":"我是十八怪"
},
{
"src":"5.png",
"title":"我是十八怪"
},
{ "src":"6.png",
"title":"我是十八怪"
},
{
"src":"7.png",
"title":"我是十八怪"
},
{
"src":"8.png",
"title":"我是十八怪"
},
{
"src":"9.png",
"title":"我是十八怪"
},
{
"src":"10.png",
"title":"我是十八怪"
},
{
"src":"11.png",
"title":"我是十八怪"
},
{
"src":"12.png",
"title":"我是十八怪"
},
{
"src":"13.png",
"title":"我是十八怪"
},
{
"src":"14.png",
"title":"我是十八怪"
},
{
"src":"15.png",
"title":"我是十八怪"
},
{
"src":"16.png",
"title":"我是十八怪"
},
{
"src":"17.png",
"title":"我是十八怪"
},
{
"src":"18.png",
"title":"我是十八怪"
}]
//瀑布流函数
function waterfall(boxes,warp){
//获取图片盒子的宽度
var boxWidth=boxes[0].offsetWidth+20;
//获取浏览器客户化宽度(这个宽度是动态变化的,随着浏览器窗口的大小变化)
var bodyWidth=document.documentElement.clientWidth;
//规定每一行可以存放的图片盒子的列数,不足一列,向上取整
var col=Math.floor(bodyWidth/boxWidth);
//外层父容器的宽度,设置后结合之前的CSS设置,可以让warp父盒子左右居中
warp.style.width=boxWidth*col+'px';
//将每一个图片盒子的高度放进一个数组,并找到这个数组中最小高度和最小高度所在的数组索引
//新建一个数组
var everyHeight=new Array();
//循环将图片盒子的高度赋值给数组
for(var i=0;i<boxes.length;i++)
{
//若在第一行,直接将图片盒子高度赋值给数组
if (i<col) {
//能够放在第一行的盒子高度放进数组,这个数组里面的高度值和个数是动态变化的,个数随着浏览器可视宽度变化而变化
//数组值会随着每次设置定位样式后更新高度值而变化,从而可以让每个当前进来的盒子总能当前最小高度盒子而对自身进行定位
//这是瀑布流函数的核心思想:通过动态数组,比较动态数组的中存放的高度值,让每个当前进来的盒子实现精准定位
everyHeight[i]=boxes[i].offsetHeight+20;
}else{
//第二行开始,先找到数组中高度的最小值
var minHeight=Math.min.apply(null,everyHeight);
//获取数组高度最小值所在索引
var minIndex=getIndex(everyHeight,minHeight);
//为当前图片盒子布局找位置,设置绝对定位,设置left和top的值,需要加上单位px
var leftValue=boxes[minIndex].offsetLeft-10+'px';
var topValue=minHeight+'px';
//将设置样式封装成函数并调用,注意传参的正确性
setStyle(boxes[i],i,leftValue,topValue);
//更新最小高度索引列高度,方便下一个盒子进来后再次比较高度
everyHeight[minIndex]+=boxes[i].offsetHeight+20;
}
}
}
//设置样式函数
var getBoxNumber=0;
function setStyle(box,index,left,top){
//通过赋值刷新操作,得到上一次设置样式的盒子的索引值getBoxNumber,
//判断当前需要设置样式的盒子的索引是否大于等于getBoxNumber,
//从而减少每次调用瀑布流函数时,因WRAP盒子里div盒子的追加,重复刷新之前设置样式定位的盒子
//需要注意函数传参,各参数的准确性和上个瀑布流函数的变量值是否能正确传进来
if(index>=getBoxNumber){
box.style.position='absolute';
box.style.left=left;
box.style.top=top;
//刷新getBoxNumber的值,得到已经设置好定位的盒子索引,并与后续进来的盒子索引作比较,以便判断是否设置定位样式
getBoxNumber=index;
}else{
return false;
}
}
//获取数组高度索引函数
function getIndex(arr,height){
//循环遍历数组,找到数组中高度最小值的索引返回
for(var index in arr){
if (arr[index]==height) {
return index;
}
}
}
//追加时机判断
function addStart(){
//获取文档滚动的高度,scrollTop经测试表示为滚动条滚动时,文档内容移动的高度,非滚动条移动的高度
//为了兼容性,需要携程如下形式,否则有可能获取的值一直是0
var scrollInstance=document.body.scrollTop||document.documentElement.scrollTop;
//获取文档高度,clientHeight表示当前可视窗口文档的高度,不包括浏览器的菜单,滚动条的高度
//当不明确这些属性值得含义时,最好的方式是去测试
var documentHeight=document.body.offsetHeight||document.documentElement.clientHeight;
//获取最后一个盒子的高度,因为要找到最后一个盒子,此函数没有传参,需要重新获取盒子的DOM对象
var boxes=document.getElementsByClassName('box');
//获取最后一个盒子的高度,offsetHeight的含义是盒子上下外边框之间的距离,需要加上外边距才是盒子的高度
var lastHeight=boxes[boxes.length-1].offsetHeight+20;
//获取最后一个盒子的top值,offsetTop官方含义是盒子边框左上角顶点到其父元素边框左上角顶点的垂直偏移量
var lastTop=boxes[boxes.length-1].offsetTop-10;
//当文档高度+滚动条高度大于最后一个盒子的高度+最后一盒子的TOP值,才开始追加
if (documentHeight+scrollInstance>=lastTop+lastHeight) {
return true;
}else{
return false;
}
}
//追加盒子函数
function appendBox(warp){
//循环遍历数组data数据,每一次添加一个div盒子,并更新容器WRAP
for(var i in data){
var stringData='<div class="box"><img src="images/'+data[i].src+'"><a href="">'+data[i].title+'</a></div>'
warp.innerHTML+=stringData;
}
//console.log(warp);
//调用瀑布流函数,warp容易里的盒子因追加后数量更新,需重新获取Dom对象,此处不能使用boxes
waterfall(warp.getElementsByTagName('div'),warp);
}
//主函数
window.onload=function(){
//获取容器和盒子的DOM对象
var warp=document.getElementById('warp');
var boxes=warp.getElementsByTagName('div');
//调用瀑布流函数
waterfall(boxes,warp);
//绑定滚动条事件。
document.onscroll=function(){
//监听滚动条位置,当追加函数返回为真时,才开始追加
if(addStart()){
//调用追加函数
appendBox(warp,boxes);
}
}
}
效果图: