<div id="all">
<div class="box"><div class="pic"><img src="images/img01.jpg" alt=""></div></div>
<div class="box"><div class="pic"><img src="images/img02.jpg" alt=""></div></div>
<div class="box"><div class="pic"><img src="images/img03.jpg" alt=""></div></div>
<div class="box"><div class="pic"><img src="images/img04.jpg" alt=""></div></div>
<div class="box"><div class="pic"><img src="images/img05.jpg" alt=""></div></div>
</div>
布局:最外层由一个大的盒子包裹,里面的图片被两层div包裹,具体的布局效果可以参照上面的图片,其中class为box盒子指的是图中的红色区域,这样处理的好处是便于确认left值,由于每个小盒子都是定位出来的,用盒子的宽度值乘以当前盒子位于的列数,便可计算出left值。
css:设置box元素的上左的padding值,再设置pic元素的padding值便可实现上图的效果,总体来说瀑布流效果的布局和样式还是比较容易的,只需要确定box元素的区域即可。
*{margin: 0;padding: 0;border: none;}
img{vertical-align: top;}
#all{position: relative;}
.box{
float: left;
padding: 15px 0 0 15px;
}
.pic{
padding: 10px;
border: 1px solid #ccc;
background-color: #fff;
border-radius: 5px;
}
.pic img{
width: 165px;
}
接下来就是逻辑的处理,步骤如下。
首先,我们需要将最外层的盒子居中显示,也就是class为all的区域,若想让块级元素居中显示,我们就要确定all的宽度,用屏幕的宽度除以每个子盒子的宽度可以求出图片的列数,再用列数乘以每个盒子的宽度即可求出all的width属性值。
其次,我们需要定位除了第一行以外的子盒子,定位的核心思想是将第二行的第一个图片衔接到第一行高度最小盒子的底部,此时我们需要获取第一行每个盒子的高度,将这些数值置于数组中,借用underscore类库的._min方法找到数组中的最小值,遍历数组,找到与最小值相等的数组的索引,确认最小高度值后便可定位衔接图片的top和left值,top值比较容易确认,就是最小高度值,left值可以通过当前盒子的索引乘以每个盒子的宽度来确定。以上步骤实现完毕后,不要忘记更新数组(否则所有的盒子都会重叠到一起)更新的方法不是很复杂,只需要用最小值加上当前盒子的高度。
function waterFull(parent,child){
//1.让父盒子居中显示
//1.1获取所有的子盒子
var allBox=$(parent).getElementsByClassName(child);
//1.2获取子盒子宽度
var boxWidth=allBox[0].offsetWidth;
//1.3获取屏幕的宽度
var screenX=document.documentElement.clientWidth;
//1.4获取列数
var cols=parseInt(screenX/boxWidth);
//1.5父盒子宽度确定并居中显示
$(parent).style.width=cols*boxWidth+'px';
$(parent).style.margin='0 auto';
//2.子盒子定位
//2.1定义各种变量
var heightArr=[],boxHeight=0,minBoxHeight=0,minBoxIndex=0;
//2.2遍历所有图片
for(var i=0;i<allBox.length;i++){
//2.2.1获取每个图片的高度
boxHeight=allBox[i].offsetHeight;
if(i<cols){//处理第一行图片
heightArr.push(boxHeight);
}else{//其余图片的处理
//2.2.2确定最小图片的高度
minBoxHeight=_.min(heightArr);
//2.2.3确定最小盒子的索引
minBoxIndex=getMinBoxIndex(heightArr,minBoxHeight);
//2.2.4定位子盒子
allBox[i].style.position='absolute';
allBox[i].style.top=minBoxHeight+'px';
allBox[i].style.left=minBoxIndex*boxWidth+'px';
//2.2.5重新定义高度
heightArr[minBoxIndex]+=boxHeight;
}
}
}
//获取最小盒子的索引
function getMinBoxIndex(arr,val){
for(var i=0;i<arr.length;i++){
if(arr[i]===val){
return i;
}
}
}
盒子布局完毕后,即可开始实现瀑布流效果,当滚动条滚动到最后一个盒子一半位置时,开始加载后面的图片(规则可以自己制定,不需要统一)如果最后盒子距离顶部的值小于等于屏幕高度与滚动高度的总和时,可以执行加载函数。这里不要忘记加上滚动的高度。
function loadImg(){
//1.获取最后盒子
var allBox=$('all').getElementsByClassName('box');
var lastBox=allBox[allBox.length-1];
//2.计算出最后盒子的高度
var lastBoxH=lastBox.offsetTop+lastBox.offsetHeight*0.5;
//2.1计算屏幕高度
var screenH=document.documentElement.clientHeight;
//2.2计算滚动高度
var scrollH=scroll().top;
//2.3返回对比结果
return lastBoxH<=screenH+scrollH;
}
加载的数据可以动态传入,在这里我就自己定义了一些数据,方便操作。节点的创建和添加比较容易,这里就不再叙述了,节点创建完毕后需要重新进行瀑布流布局,这一步至关重要。加入定时器只是为了实现缓动加载的效果,可以自主选择加或不加。
window.onload=function(){
//1.瀑布流布局
waterFull('all','box');
//2.动态加载图片
var time1=null;
window.onscroll=function(){
clearTimeout(time1);
time1=setInterval(function(){
if(loadImg()){
//2.1创建数据
var imgData=[
{'src':'images/img01.jpg'},
{'src':'images/img10.jpg'},
{'src':'images/img17.jpg'},
{'src':'images/img16.jpg'},
{'src':'images/img30.jpg'},
{'src':'images/img21.jpg'}
];
//2.2不断加载数据
for(var i=0;i<imgData.length;i++){
var newBox=document.createElement('div');
newBox.className='box';
$('all').appendChild(newBox);
var newImg=document.createElement('div');
newImg.className='pic';
newBox.appendChild(newImg);
var newData=document.createElement('img');
newData.src=imgData[i].src;
newImg.appendChild(newData);
}
//2.3重新布局
waterFull('all','box');
}
},200);
};
//2.4窗口大小变化
var time2=null;
window.onresize=function(){
clearTimeout(time2);
time2=setTimeout(function(){
waterFull('all','box');
},200);
}
}
利用JQuery实现瀑布流,将上面的JS代码改写成JQ的即可
注意点:$.inArray() 函数用于在数组中查找指定值
$(function(){
//1.瀑布流布局
waterFull();
//2.动态加载图片
var time1=null;
$(window).on('scroll',function(){
clearTimeout(time1);
time1=setInterval(function(){
if(loadImg()){
//2.1创建数据
var imgData=[
{'src':'images/1.jpg'},
{'src':'images/10.jpg'},
{'src':'images/17.jpg'},
{'src':'images/16.jpg'},
{'src':'images/30.jpg'},
{'src':'images/21.jpg'}
];
//2.2不断加载数据
$.each(imgData,function(index,value){
var newBox=$('<div></div>').addClass('box').appendTo('#all');
var newPic=$('<div></div>').addClass('pic').appendTo(newBox);
$('<img/>').attr('src',$(value).attr('src')).appendTo(newPic);
})
//2.3重新布局
waterFull();
}
},200);
});
});
/**
* 实现瀑布流的布局
*/
function waterFull(){
//1.让父盒子居中显示
//1.1获取所有的子盒子
var allBox=$('#all>.box');
//1.2获取子盒子宽度
var boxWidth=allBox.eq(0).outerWidth();
//1.3获取屏幕的宽度
var screenX=$(window).width();
//1.4获取列数
var cols=parseInt(screenX/boxWidth);
//1.5父盒子宽度确定并居中显示
$('#all').css({
width:cols*boxWidth+'px',
margin:'0 auto'
})
//2.子盒子定位
//2.1定义各种变量
var heightArr=[],boxHeight=0,minBoxHeight=0,minBoxIndex=0;
//2.2遍历所有图片
$.each(allBox,function(index,value){
boxHeight=$(value).outerHeight();
if(index<cols){//处理第一行图片
heightArr.push(boxHeight);
}else{//其余图片的处理
//2.2.2确定最小图片的高度
minBoxHeight=Math.min.apply(this,heightArr);
//2.2.3确定最小盒子的索引
minBoxIndex=$.inArray(minBoxHeight,heightArr);
//2.2.4定位子盒子
//2.2.1获取每个图片的高度
$(value).css({
position:'absolute',
left:minBoxIndex*boxWidth+'px',
top:minBoxHeight+'px'
})
//2.2.5重新定义高度
heightArr[minBoxIndex]+=boxHeight;
}
});
}
function loadImg(){
//1.获取最后盒子
var lastBox=$('#all>.box').last();
//2.计算出最后盒子的高度
var lastBoxH=lastBox.outerHeight()+lastBox.offset().top*0.5;
//2.1计算屏幕高度
var screenH=$(window).height();
//2.2计算滚动高度
var scrollH=$(window).scrollTop();
//2.3返回对比结果
return lastBoxH<=screenH+scrollH;
}