懒加载 & 瀑布流布局
前几天星星零零的学习,终于搞定了懒加载和瀑布流布局,下面我们通过这篇文章一起学习和探讨一下~
懒加载
首先,什么是懒加载呢?原理?
其原理是:将图片的 src
设置为统一的一张默认图片 / loading图 / logo / 空白图片(这样做会使网络请求只有一条哦),可以将真实图片的 src
存储到 img
标签的自定义属性 data-src
中,当图片出现在用户视野 / 可视窗口中(需要展示出来)时,即将自定义属性 data-src
中 的地址替换 src
中的默认图片地址,达到懒加载的效果。
那这么做是为了什么呢?本质?
这个通过原理也很容易看出来,如果页面存在大量的图片,那并不是所有的图片在一开始的时候都要加载完毕,这样浏览器的压力太大了。所以懒加载的本质就是为了,缓解浏览器的压力,提升用户体验,防止页面一次性向服务器响应大量请求导致服务器响应慢,页面卡顿或崩溃等问题。
这里我们需要解决两个问题
1. 如何判断图片是否出现在用户的视野 / 可视窗口中?
上图!
是否一目了然
判断一个元素是否出现在用户视野 / 可视窗口只需要判断是否
$view.height() + $view.scrollTop() >= $node.offset().top
即窗口的高度加上窗口滚动的距离是否大于等于元素距离容器顶部的距离,如果是,则该元素出现(需要加载),否则没有出现(不需要加载)
2. 如何加载图片?
这个问题在原理处就进行了阐述,可以将真实图片的 src
放到自定义属性 data-src
中,当判断图片出现在了可视窗口中,将自定义属性中的值拿出替换 img
标签的 src
或者我们可以将真实图片的 src
存储到数组中,通过下标访问
其实这之中还存在一些问题,比如
加载过的图片在 scroll
上去再次出现在可视窗口,不需要重复修改 src
了
这个问题我们同样可以在 img
标签中添加自定义属性标志该图片已经加载完毕 $node.attr('data-isLoaded', 1)
,然后在滚动触发加载图片时判断该图片是否已经加载完毕
同时给滚动事件设置函数节流,避免极短时间内很多重复执行
下面给出懒加载完整代码:
start()
var clock = null
$(window).on('scroll', function() {
if (clock) clearTimeout(clock)
clock = setTimeout(function() {
start()
}, 300);
})
function start() {
$('.container img').not('[data-isLoaded]').each(function() {
var $node = $(this)
if ( isShow($node) ) {
loading($node)
}
})
}
function loading($node) {
$node.attr('src', $node.attr('data-src'))
$node.attr('data-isLoaded', 1)
}
function isShow($node) {
return $node.offset().top <= $(window).height() + $(window).scrollTop()
}
瀑布流布局
蘑菇街网瀑布流布局
看了上面两张截图,对瀑布流有没有一些认识呢?
其中的共同点是不是:固定宽度,高度不定,间隙均匀的各元素,一行中从左到右排列,一行排满之后,其余会按顺序排在最短的一列后
这样做有什么好处呢?
1、吸引用户
当用户在浏览瀑布流式布局的时候(这里抛开懒加载),用户会产生一种错觉,就是信息是不停的在更新的,这会激发用户的好奇心,使用户不停的往下滑动。
2.良好视觉体验
采用瀑布流布局的方式可以打破常规网站布局排版,给用户眼前一亮的新鲜感,用户在浏览内容时会感到很有新鲜感,带来良好的视觉体验。
3.更好的适应移动端
由于移动设备屏幕比电脑小,一个屏幕显示的内容不会非常多,因此可能要经常翻页。而在建网站时使用瀑布流布局,用户则只需要进行滚动就能够不断浏览内容。(这一点和懒加载有一点像)
那么瀑布流如何实现?原理?
简单的来说:
- 对需要瀑布流布局的元素绝对定位,其父元素position: relative。
- 通过计算父元素(窗口或者显示容器)宽度除以需要瀑布流布局的元素宽度得到瀑布流布局的列数,并对存储每列高度值的数组进行初始化
- 摆放元素时,比较每列高度,每次选取高度最低的一列摆放一个元素,通过设置元素css top 和 left
- 重复3直到元素全部摆放完毕
这里可以利用元素宽度及其包裹元素的外边距 $node.outerWidth(true)
来计算元素占据的全部宽度
下面是JS代码:
var colCount
var colHeightArray = []
var imgWidth = $('.waterfall img').outerWidth(true)
colCount = Math.floor($(window).width() / imgWidth)
for (var i = 0; i < colCount; i++) {
colHeightArray[i] = 0
}
$('.waterfall img').on('load', function() {
var minValue = colHeightArray[0]
var minIndex = 0
for (var i = 0; i < colCount; i++) {
if (colHeightArray[i] < minValue) {
minValue = colHeightArray[i]
minIndex = i
}
}
$(this).css({
left: minIndex * imgWidth,
top: minValue
})
colHeightArray[minIndex] += $(this).outerHeight(true)
})
改进:
var waterfall = {
init: function() {
this.clock = null
this.colHeightArray = []
this.imgWidth = $('.waterfall img').outerWidth(true)
this.colCount = Math.floor($(window).width() / this.imgWidth)
for (var i = 0; i < this.colCount; i++) {
this.colHeightArray[i] = 0
}
this.bind()
},
bind: function() {
var _this = this
$('.waterfall img').on('load', function() {
_this.layout($(this))
})
$(window).on('resize', function() {
if (_this.clock) clearTimeout(_this.clock)
_this.colCount = Math.floor($(window).width() / _this.imgWidth)
for (var i = 0; i < _this.colCount; i++) {
_this.colHeightArray[i] = 0
}
_this.clock = setTimeout(function() {
$('.waterfall img').each(function() {
_this.layout($(this))
})
}, 300);
})
},
layout: function($node) {
var minValue = this.colHeightArray[0]
var minIndex = 0
for (var i = 0; i < this.colCount; i++) {
if (this.colHeightArray[i] < minValue) {
minValue = this.colHeightArray[i]
minIndex = i
}
}
$node.css({
left: minIndex * this.imgWidth,
top: minValue
})
this.colHeightArray[minIndex] += $node.outerHeight(true)
}
}
waterfall.init()
详细可以参考:
原生JS实现瀑布流
应用:瀑布流新浪新闻
waterfall-sinaNews要如何实现这个效果呢?
我们在这里理一下思路
- 首先
页面第一次加载:
1.ajax发送 page = 1 perPageCount = 10 请求获取数据
2.将请求到的数据拼接成 dom 放到页面中
3.使用瀑布流布局去摆放元素
4.page++ - 滚动到底部
再次加载数据
1.ajax发送page perPageCount 请求获取数据
2.将请求到的数据拼接成 dom 放到页面中
3.使用瀑布流布局去摆放元素
4.page++
这里我们需要知道,怎么判断是否到达底部了?
从图中我们得知,当到达底部时
$view.height() + $view.scrollTop() = $content.height()
所以我们可以利用
$view.height() + $view.scrollTop() +20px >= $content.height()
判断是否到达了底部,需要再次请求数据加载数据了