lazyLoadAndDebounce

如何实现图片懒加载?

思路:

  1. 存储图片地址,可利用DOM中自带的dataset属性
  2. 等用户可视窗口进入图片区域后,通过存储在dataset中的图片地址替换img的src属性
  3. 移除dataset中存储的地址

简单实现

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
window.addEventListener('scroll', lazyLoad);
const images = document.querySelectorAll('img');
var n = 0;
const lazyLoad = function (e) {
for (var i = n; i < images.length; i++) {
const img = images[i];
// 当图片一半以上暴露在可视窗口,执行下列
const showAt = window.pageYOffset + window.innerHeight - img.height / 2;
const isAtHalf = showAt > img.offsetTop;
if (isAtHalf) {
let src = img.dataset.src;
img.setAttribute('src', src);
img.removeAttribute('data-src');
n++;
}
}

Note: 需要注意的是, 这里利用了变量n来判断图片是否加载完成,避免在滚动时重复执行造成undefined错误。 因为当任意图片再次执行此for循环时,第一次循环已经清除了data-src属性,因此会报undefinded错误。

问题

上述方法中,当用户滚动时,事件监听函数会频繁触发,因此可能会造成不必要的性能损耗,因此就需要防抖函数来限制其触发频率。

防抖函数

思路:

  1. 初次触发时,立即执行回调函数
  2. 在事件第二次被触发n秒之后,再执行回调函数
  3. 如果在n秒内,事件再次被触发,重新计时
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
function debounce(fn,time=500,immediate=true){
var timer = null;
return function(){
var _self = this,
args = arguments;
// 首先清除定时器
clearTimeout(timer);
var cb = function () {
// n秒之后把timer重新设为null
timer = null;
// 第一次不立即执行逻辑
if (!triggerNow) fn.apply(_self, args);
};
if (triggerNow && !timer) {
// 只有timer为null时才进入此函数体
fn.apply(_self, args);
}
timer = setTimeout(cb, time);
}
}

节流函数

与防抖函数类似,节流函数在每n秒之内只执行一次

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
function throttle(fn,time=1000){
var timer = null,
begin = new Date().getTime();
return function(){
var _self = this,
args = arguments;
cur = new Date().getTime();
clearTimeout(timer);
if(cur-begin >= time){
fn.apply(_self,args);
begin = cur;
}else{
timer = setTimeout(()=>{
fn.apply(_self,args);
},time)
}
}
}

应用场景:

  • Ajax 请求数据提交
  • 表单验证
  • 监听滚动,鼠标悬浮事件等

如何实现图片预加载?

思路:

  1. 通过Image构造函数循环图片地址
  2. 给每一个实例化出来的图片对象添加onload事件
  3. 当触发onload事件时,把此图片添加到DOM节点上去

简单实现

1
2
3
4
5
6
7
8
9
const container = document.querySelector('.container');
const images = ['http://unsplash.it/200/200','http://unsplash.it/300/300','http://unsplash.it/400/400'];
images.forEach(image=>{
let img = new Image();
img.src = image;
img.onload = function(){
container.appendChild(img);
}
})

同样可以使用link标签的rel='preload'属性在页面被渲染之前就把图片加载完成,这种方法也适用于预加载js,css,大型媒体文件等。

1
2
3
4
5
6
7
8
9
10
<head>
<meta charset="utf-8">
<link rel="preload" href="1.png" as="image">
<link rel="preload" href="main.js" as="script">
</head>
<body>
<div>
<img src='1.png'>
</div>
</body>
  • 版权声明: 本博客所有文章除特别声明外,均采用 Apache License 2.0 许可协议。转载请注明出处!
  • © 2020 Ruoyu Wang
  • PV: UV:

请我喝杯咖啡吧~

支付宝
微信