跳到主要内容

30.6 节流器与图片延迟加载

节流器旧版
var throttle = function () {
// 获取第一个参数
var isClear = arguments[0],
fn
// 如果第一个参数是 boolean 类型那么第一个参数则表示是否清除计时器
if (typeof isClear === 'boolean') {
// 第二个参数则为函数
fn = arguments[1]
// 函数的计时器句柄存在,则清除该计时器
fn.__throttleID && clearTimeout(fn.__throttleID)
// 通过计时器延迟函数的执行
} else {
// 第一个参数为函数
fn = isClear
// 第二个参数为函数执行时的参数
param = arguments[1]
// 对执行时的参数适配默认值,这里我们用到以前学过的extend方法
var p = extend(
{
context: null,
args: [],
time: 300,
},
param
)
console.log('p', p)
// 清除执行函数计时器句柄,callee现在已经过期
arguments.callee(true, fn)
// 为函数绑定计时器句柄,延迟执行函数
fn.__throttleID = setTimeout(function () {
// 执行函数
fn.apply(p.context, p.args)
}, p.time)
}
}
节流器改成ES6新版
// 节流器ES6版
const throttleES6 = function (...arg) {
// 获取第一个参数
const isClear = arg[0]
let fn = null
// 如果第一个参数是 boolean 类型那么第一个参数则表示是否清除计时器
if (typeof isClear === 'boolean') {
// 第二个参数则为函数
fn = arg[1]
// 函数的计时器句柄存在,则清除该计时器
console.log('清除一次', fn.__throttleID)
fn.__throttleID && clearTimeout(fn.__throttleID)
// 通过计时器延迟函数的执行
} else {
// 第一个参数为函数,就是要执行加载图片的函数
fn = isClear
// 第二个参数为函数执行时的参数
param = arg[1]
// 对执行时的参数适配默认值,这里我们用到以前学过的extend方法
// ==拓展参数
let p = {
context: null,
args: [],
time: 300,
...param,
}
// 清除执行函数计时器句柄,如果存在的话清除一次setTimeout计时器
// 递归
throttleES6(true, fn)
// 为函数绑定计时器句柄,延迟执行函数
fn.__throttleID = setTimeout(function () {
// 执行函数
fn.apply(p.context, p.args)
}, p.time)
}
}
图片延迟加载
/**
* 节流延迟加载图片类
* param id 延迟加载图片的容器 id
* 注,图片格式如下 <img src="img/loading.gif" alt="" data-src="img/1.jpg" />
*/
function LazyLoad(id) {
// 获取需要节流延迟加载图片的容器
this.container = document.getElementById(id)
// 缓存图片
this.imgs = this.getImgs()
// 执行逻辑
this.init()
}
// 节流延迟加载图片类原型方法
LazyLoad.prototype = {
// 起始执行逻辑
init: function () {
// 第一次打开直接显示
this.update()
// 下拉加载更多
this.bindEvent()
},
// 获取图片列表
getImgs: function () {
// 新数组容器
var arr = []
// 获取图片
var imgs = this.container.getElementsByTagName('img')
// 将获取的图片转换为数组(IE下通过Array.protoytpe.slice 会报错)
for (var i = 0, len = imgs.length; i < len; i++) {
arr.push(imgs[i])
}
return arr
},
// 加载图片
update: function () {
// 如果图片都加载完成,返回
if (!this.imgs.length) {
return
}
// 获取图片长度
var i = this.imgs.length
// 遍历图片
for (--i; i >= 0; i--) {
// 如果图片在可视范围内
if (this.shouldShow(i)) {
// 加载图片
this.imgs[i].src = this.imgs[i].getAttribute('data-src')
// 清除缓存中的此图片
this.imgs.splice(i, 1)
}
}
},
// 判断图片是否在可视范围内
shouldShow: function (i) {
// 获取当前图片
var img = this.imgs[i]
// 可视范围内顶部高度(页面滚动条 top 值)
var scrollTop =
document.documentElement.scrollTop || document.body.scrollTop
// 可视范围内底部高度
var scrollBottom = scrollTop + document.documentElement.clientHeight
// 图片的顶部位置
var imgTop = this.pageY(img)
// 图片的底部位置
var imgBottom = imgTop + img.offsetHeight
// 判断图片是否在可视范围内:图片底部高度大于可视视图顶部高度并且图片底部高度小于可视视图底部高度,
// 或者图片顶部高度大于可视视图顶部高度并且图片顶部高度小于可视视图底部高度
if (
(imgBottom > scrollTop && imgBottom < scrollBottom) ||
(imgTop > scrollTop && imgTop < scrollBottom)
) {
return true
}
// 不满足上面条件则返回false
return false
},
// 获取元素页面中的纵坐标位置
pageY: function (element) {
// 如果元素有父元素
if (element.offsetParent) {
// 返回元素+父元素高度
return element.offsetTop + this.pageY(element.offsetParent)
} else {
// 或者返回元素高度
return element.offsetTop
}
},
// 绑定事件(简化版)
on: function (element, type, fn) {
if (element.addEventListener) {
element.addEventListener(type, fn, false)
} else {
element.attachEvent('on' + type, fn, false)
}
},
// 为窗口绑定resize事件与scroll事件
bindEvent: function () {
var that = this
this.on(window, 'resize', function () {
// 节流处理更新图片逻辑
throttleES6(that.update, { context: that })
})
this.on(window, 'scroll', function () {
// 节流处理更新图片逻辑
throttleES6(that.update, { context: that })
})
},
}
html
<div id="container">
<ul>
<li><img src="./imgs/loading.gif" alt="" data-src="./imgs/1.jpg" /></li>
<li><img src="./imgs/loading.gif" alt="" data-src="./imgs/2.jpg" /></li>
<li><img src="./imgs/loading.gif" alt="" data-src="./imgs/3.jpg" /></li>
<li><img src="./imgs/loading.gif" alt="" data-src="./imgs/4.jpg" /></li>
<li><img src="./imgs/loading.gif" alt="" data-src="./imgs/5.jpg" /></li>
<li><img src="./imgs/loading.gif" alt="" data-src="./imgs/6.jpg" /></li>
</ul>
</div>
执行
// 延迟加载container容器内的图片
const lazyload = new LazyLoad('container')
console.log(lazyload)