JS实现瀑布流
基本结构
每一张图片,外层是不可见的box,中间是可见的pic装着img,要求盒子宽度固定。
距离控制
要使多个盒子间隙相同时,指定向左浮动并且设定box的上左内边距即可,因为box不可见,实现的效果就类似于pic互相间隔开。
排列规则
先固定第一行,然后按照第一行的宽高,将后续图片插入上一行最短的图片下面。
1. 获取文档宽度和每个盒子宽度(此处取第一个盒子),然后相除获得一行排列盒子的个数,将容器设置为盒子。
2. 定位第一行盒子:top为0,left为单个盒子宽度×盒子顺序的下标,将每一列盒子的高度存储在一个数组中。
3. 获取第一行盒子高度的最小值(Math.min),然后依次比较确定是第几个盒子,对于顺序大于cnum的盒子,即第一行以后,位置即在第一行最短的盒子之下,定位为absolute,top值为最短盒子的高度,left值和上面的最短盒子一样。
4. 然后将盒子高度的数组相应列数的高度值更新,继续排列下一个盒子。
更新图片
当滚动条拉到最后张图片露出一半时,加载剩余的图片。
A 是最后一个 box 盒子的上边距(offsetTop),B 是最后一个盒子高度的一半,C 是用户拉动滚轴的长度(scrollTop),D 是页面高度(clientHeight)。从上图可以看出,当最后的 box 盒子没出到一半时满足
A+B>C+D
而当最后一个盒子出来一半时应该加载剩余图片了,此时A+B<C+D。
1. 分别获取A,B,C,D的值,然后比较判断,符合要求时,开始加载数据。
2. 数据储存在json数组中或者从服务器端请求等等,创建一个盒子and里面的图片等等,加入到容器中,然后调用瀑布流的布局函数进行布局。
适应浏览器宽度变化
一行能排列的盒子个数在文档刚加载完成时就固定了。当后续改变宽度时,需要随之进行改变。
在window.onresize时调用waterfall函数。
完整代码(计蒜客):
var dataInit = {
'data':[
{'src':'http://res.jisuanke.com/img/upload/20160314/5a2926df15a72b5db4b64a262a3fdca37b67b5c5.jpeg'}
, {'src':'http://res.jisuanke.com/img/upload/20160314/9dd53e614ca27e4861cdadb28cc28589b7fbd95a.jpeg'}
, {'src':'http://res.jisuanke.com/img/upload/20160314/b58cda8cbf922faab29e9ce380e5894d84630eae.jpeg'}
, {'src':'http://res.jisuanke.com/img/upload/20160314/2820b7ccd8cc33cf721cc71173963e7c91da9e68.jpeg'}
, {'src':'http://res.jisuanke.com/img/upload/20160314/8462d92a3e4229b2a64f486f3f625b4561268823.jpeg'}
, {'src':'http://res.jisuanke.com/img/upload/20160314/09c69d8f31ca72526211425fd15323ac75fbb9f7.jpeg'}
]
};
function waterfall(parent, sclass) {
var oParent = document.getElementById(parent);
var aBox = document.getElementsByClassName(sclass);
var boxwidth = aBox[0].offsetWidth;
var documentwidth = document.documentElement.clientWidth;
var cnum = Math.floor(documentwidth/boxwidth);
oParent.style.width = boxwidth * cnum + 'px';
var aBoxHeight = new Array();
for(var i = 0; i < aBox.length; i++) {
if(i < cnum) {
aBox[i].style.top = 0 + 'px';
aBox[i].style.left = boxwidth * i + 'px';
aBoxHeight.push(aBox[i].offsetHeight);
}
else {
var minHeight = Math.min.apply(null, aBoxHeight);
var minIndex = getIndex(aBoxHeight, minHeight);
aBox[i].style.position = 'absolute';
aBox[i].style.top = minHeight + 'px';
aBox[i].style.left = aBox[minIndex].offsetLeft + 'px';
aBoxHeight[minIndex] += aBox[i].offsetHeight;
}
}
}
function getIndex(arr, value) {
for(var i in arr) {
if(arr[i] == value) return i;
}
}
window.onload = function() {
waterfall('main', 'box');
}
function checkScrollside(sClass) {
var aBox = document.getElementsByClassName(sClass);
var lastImgIn = aBox[aBox.length-1].offsetTop + Math.floor(aBox[aBox.length-1].offsetHeight/2);
var scrollTop = document.documentElement.scrollTop || document.body.scrollTop;
var documentHeight = document.documentElement.clientHeight || document.body.clientHeight;
return (lastImgIn < scrollTop + documentHeight);
}
window.onscroll = function() {
if(checkScrollside('box')) {
var oParent = document.getElementById('main');
for(var i = 0; i < dataInit.data.length; i++) {
var oBox = document.createElement('div');
oBox.className = 'box';
var oPic = document.createElement('div');
oPic.className = 'pic';
var oImg = document.createElement('img');
oImg.src = dataInit.data[i].src;
oPic.appendChild(oImg);
oBox.appendChild(oPic);
oParent.appendChild(oBox);
}
waterfall('main', 'box');
}
}
window.onresize = function() {
waterfall('main', 'box');
}