瀑布流实现方法及其实践
瀑布流布局
瀑布流概念,页面内图片等宽不等高,每张图片放置于当前页面高度最小的图片列之下,且在鼠标滚动到页面底部后,实现自动加载
以上有两个核心点,也是实现瀑布流效果的关键
1、 每张图片放置于当前页面高度最小的图片列之下
思路:除第一行图片外所有图片采用绝对定位的方式,每张图片的位置取决于它上面一张图片的位置和当前列的高度
定义一个新数组,用于放置图片列的高度。长度为图片列数。
首先放置好第一行图片,数组的值为第一行图片的高度
var picH = [];
//picBox是所有图片的JQ对象
picBox.each(function(index, value) {
//each循环默认两个参数,一个是循环的索引,一个是循环的节点,这里是pixBox数组中每一个对象(且是html对象!!)
var h = picBox.eq(index).outerHeight();
if (index < cols) {
//第一行图片
picH[index] = h;
}
从第二行起,在图片列高度最小处放置图片,获取当前新的图片列高度(图片高度累加),并更新数组(替换最小值)
var minH = Math.min.apply(null, picH);//取数组中的最小值
var Minindex = $.inArray(minH, picH); //$.inArray是取某个值在数组中的索引的方法
//为当前图片添加样式,放置于合适的位置
$(value).css({
'position': 'absolute',
"top": picH[Minindex] + 'px',
'left': picBoxW * Minindex
})
//获取当前的列高度,并更新数组
picH[Minindex] += $(value).outerHeight() ;
这样就能实现所有图片等宽不等高,且能铺满整个屏幕
在鼠标滚动到页面底部后,实现自动加载
原理:获取页面最后一个元素与窗口顶部的距离,若这个距离小于窗口高度,则说明这个元素已经出现在了窗口中,此时执行继续加载图片的行为。
页面最后一个元素与窗口顶部的距离 = 元素与页面顶部的距离 - 鼠标滚轮向上滚动的高度
lastDiv = offset().top - scrollTop()
优化方案:最后一个元素在页面中出现一半再开始进行加载
lastDiv = offset().top + Math.floor(last.height() / 2) - scrollTop()
完整滚动判断函数如下:
//滚动加载函数
function checkScrollSlide() {
var last = $('.pic').last() //取数组中最后一个元素
var windowH = $(window).height(); //窗口高度
var scrollTop = $(window).scrollTop(); //向上滚动的高度
var lastDivMove = Math.floor(last.height() / 2) + last.get(0).offsetTop; //offset方法返回元素距离页面的偏移,这里返回向上的偏移量。.get(0)方法代表转为DOM对象
return (lastDivMove < windowH + scrollTop) ? true : false;
}
监听鼠标滚动事件
//滚动加载
$(window).on('scroll', function() {
if (checkScrollSlide()) {
<!-- 传入数据,加载内容 -->
}
})
})
关于自动加载应用拓展
自动加载的运用不仅仅局限于瀑布流。很多情况下数据或者图片比较多,而且不需要一次性加载所有数据。为了提高用户体验,就需要使用自动加载技术。
常见的加载方式有两种
第一种是在底部放置一个按钮“点击加载更多”,点击按钮继续加载数据。这种方式比较简单,只需要监听按钮的点击事件,实现数据传入。
第二种是滚动加载,通过监听鼠标滚动事件执行加载动作(本文的案例瀑布流使用的是这种方式)。可以在底部放置一个p标签设置文本“下拉加载”字样,作为引导用户下拉的提示,同时也可以作为计算滚动加载的标记元素(即页面的最后一个元素)
imageiscroll.js插件的使用
上述方法固然可行,并且无论是Pc端还是移动端都能实现下拉加载的功能,但是鼠标滚轮事件的监听动作在移动端使用的时候未免显得有些僵硬。在这个用户体验至上的时代,这并不是我们想要的最优效果。
这时候就可以运用移动端滚动插件iscroll,实现丝滑的下拉与上拉效果。
主要步骤为:
- 引入iscroll.js文件
- 实例化iScroll对象
myScroll = new iScroll('wrapper')
- 对于下拉刷新功能使用如下加载函数
function loaded() {
pullUpEl = document.getElementById('pullUp');//获取下拉刷新标签
pullUpOffset = pullUpEl.offsetHeight;
myScroll = new iScroll(
"wrapper", {
checkDOMChanges: true,
hScroll: true,
scrollbarClass: 'myScrollbar',
useTransition: false,
onRefresh: function() {
if (pullUpEl.className.match('loading') && loadOver) {
pullUpEl.className = '';
pullUpEl.innerHTML = '上拉加载更多...';
}
},
onScrollMove: function() {
if (this.y < (this.maxScrollY - 10) && !pullUpEl.className.match('flip') && loadOver) {
pullUpEl.className = 'flip';
pullUpEl.innerHTML = '松手开始更新...';
this.maxScrollY = this.maxScrollY;
} else if (this.y > (this.maxScrollY + 10) && pullUpEl.className.match('flip') && loadOver) {
pullUpEl.className = '';
pullUpEl.innerHTML = '上拉加载更多...';
this.maxScrollY = pullUpOffset;
}
},
onScrollEnd: function() {
if (pullUpEl.className.match('flip') && loadOver) {
pullUpEl.className = 'loading';
pullUpEl.innerHTML = '加载中...';
pullUpAction(); // 刷新手势结束后执行的加载内容函数
}
}
}
);
setTimeout(function() {
document.getElementById('wrapper').style.left = '0';
}, 800);
}
- 在页面中建立监听事件
document.addEventListener('touchmove', function(e) {e.preventDefault();}, false); //取消事件冒泡
document.addEventListener('DOMContentLoaded', loaded, false); //页面Dom结构加载完后执行
另外,页面中的结构要尽可能简单,最优结构如下,此时拖动的是ul标签
<div id = 'wrapper'>
<ul>
<li></li>
...
</ul>
</div>
如果有多层列表,可以使用如下结构,拖动scroll
<div id="wrapper">
<div id="scroll">
<ul>
<li></li>
...
</ul>
<ul>
<li></li>
...
</ul>
</div>
</div>