简易瀑布流布局

昨天看慕课网的视频,看到里面一节关于瀑布流布局的实现,使用的是绝对布局,显然不是最好的实现方式,于是自己实现了一下不用计算top和left值,直接用浮动布局来完成的html布局

瀑布流指的是图片或div块等宽不等高,参差无缝的排列在一起,在向下滚动滚动条到底的时候,添加新的数据到整体框里面

主要布局

.grid是外层,.col是每列,.box是每块,布局上很简单,每一列向左浮动,每一个块也向左浮动

css样式

.gird {
overflow: hidden;
}
.col {
float: left;
width: 235px;
margin-left: 15px;
}
.box {
float: left;
width: 235px;
padding-top: 15px;
}
.pic {
padding: 10px;
border: 1px solid #ccc;
border-radius: 5px;
box-shadow: 0 0 5px #ccc;
}
.image {
width: 100%;
}

html样式

<div class="gird">
<div class="col">
<div class="box">
<div class="pic">
<img src="image/1.jpg" alt="图片">
</div>
</div>
...
</div>
<div class="col">
...
</div>
</div>

js动态添加数据

动态添加数据,heightArr这个数组是用来保存每列盒子的总高度的,每次render的时候,查看每一列的heightArr是否小于body文档的高度,如果小于则添加下一张图片到imgArr[i]中,最后把imgArr[i]里面的内容拼接起来添加到对应的列中,实现了动态添加数据,正常数据是用ajax拉取的,此处暂时不考虑

var jDoc = $(document);
var heightArr = [];
var imgArr = [];
var count = 1;
var colLen = Math.floor(jDoc.width()/250);
//这个数组是保存图片信息的,此处我自己写了一些测试数据
var imgs = [0, 248, 226, 263, 282, 206, 317, 316, 290, 331, 179, 356, 270, 250, 203, 296, 168, 328, 197, 361, 172, 269, 197, 184, 253, 238, 310, 326];


function render() {
//遍历每列
for(var i = 0; i < colLen; i++) {
heightArr[i] || (heightArr[i] = 0);
imgArr[i] = [];
while(heightArr[i] <= jDoc.height()) {
//盒子高度增长
heightArr[i] += imgs[count];

imgArr[i].push('<div class="box"><div class="pic"><img class="image" src="'+url +"fall"+count+'.jpg"></div></div>');
count++;
if(count == 28) {
count = 1;
}
}
}
//向页面添加
$(".col").each(function(index) {
$(this).append(imgArr[index].join(""));
});
}

列的添加

由于需要浏览器宽度不确定,还为了适应浏览器宽度,所以列也需要动态添加

function addCols() {
var colArr = [];
for(var i = 0; i < colLen; i++) {
colArr.push('<div class="col"></div>');
}
$(".gird").append(colArr.join(""));
}

滑动添加数据的实现

需要考虑的是何时需要添加数据,思路是在最下面的盒子显示在可视窗口时,添加新的数据

具体实现是,找到当前最下面的那个盒子的距离文档顶部的距离,这里是top,找到可视窗口底边距离文档顶部的距离,这里设为winTop,如果 top > winTop 不执行,反之执行render

function scroll() {
var top = 0;
//找到最下面的盒子的offsetTop
$(".image").each(function() {
if($(this).offset().top > top) {
top = $(this).offset().top;
}
})
if(top > $(window).scrollTop() + $(window).height()) {
return;
}
render();
}

适应屏幕宽度的实现

如果容纳列数减小了,直接移除多余的列

如果容纳列数增加了,首先添加列,然后再添加数据到新添加的列中

function afresh() {
if(colLen > Math.floor(jDoc.width()/250)) {
colLen = Math.floor(jDoc.width()/250);
$(".col").each(function(index) {
if(index >= colLen) {
$(this).remove();
heightArr[index] = 0;
}
});
} else if(colLen < Math.floor(jDoc.width()/250)) {
colLen = Math.floor(jDoc.width()/250) - colLen;
addCols();

colLen = Math.floor(jDoc.width()/250);
render();
}
}

函数节流

在浏览器DOM操作是的昂贵的,而onscroll和onresize是会让事件连续触发的,所以需要对其控制,避免重复执行

解决的方式是使用定时器,throttle接收两个参数:要执行的函数以及在哪个作用域执行。第一次调用创建一个定时器,在指定时间之后运行代码,当第二次调用时,会清除前一次的定时器并设置另一个。如果前一个定时器未执行,其实就是将其替代为另一个新的定时器。目的是只有在执行函数的请求停止了一段时间之后才执行

function throttle(method, context) {
clearTimeout(method.tId);
method.tId = setTimeout(function() {
method.call(context);
}, 100);
}

总结

花了点力气做这个瀑布流,对之前的知识有了点新的认识,到没遇到特别难的问题,只是明白了解决问题的思路是可以使用一些好的设计实现的,比如说我面对滚动条向上滑执行onscroll事件这问题时,首先想到的是鼠标滚轮事件,后来想想太不合情理了,发现滚动条件这个才是合情合理的,果然思路很重要