个人封装常用方法工具包: https://github.com/lihai-boop/tool
文章已收录: https://github.com/lihai-boop/Blog
offsetTop,scrollTop,clientHeight
原生API获取距离像素计算
offsetTop:
获取当前元素与其父级元素顶部内边距的距离
scrollTop:
当前元素滚动超出的高度
clientHeight:
当前元素的可视高度
"灵魂画手"绘图
img元素offsetTop - 父级元素scrollTop<父级元素clientHeight,即说明img已滚动进入父可视区域
#view {
width: 300px;
height: 500px;
margin: 30px auto;
overflow-y: auto;
}
.parent {
height: 1000px;
}
img {
margin-top: 800px;
}
<div id="view">
<div class="parent">
<img alt="..." loading="lazy" class="img"
data-src="https://tse1-mm.cn.bing.net/th?id=OIP-C.FaG6dzohGs3q45-DwsEyQQHaEK&w=165&h=100&c=8&rs=1&qlt=90&o=6&pid=3.1&rm=2">
div>
div>
let view = document.getElementById('view');
let img = document.getElementsByClassName('img')[0];
view.onscroll = function () {
let sh = this.clientHeight;
let st = this.scrollTop;
let ch = img.offsetTop;
ch - st < sh && (img.src = img.dataset.src)
}
用户频繁滚动鼠标时,会多次触发onscroll
事件计算滚动距离,消耗性能又没有意义,所以需要控制函数执行的次数,减少性能的消耗,对滚动事件进行防抖处理
开发中为减少反复写防抖函数,已经将它封装入@lihai-js/tool
, 开箱即用
@lihai-js/tool
地址https://github.com/lihai-boop/tool
let img = document.getElementsByClassName('img')[0];
let view = document.getElementById('view');
function debounce (fn, wait = 1000, immediate = false) {
const self = this
immediate && fn.call(this)
return function () {
self.debounceTime && clearTimeout(self.debounceTime)
self.debounceTime = setTimeout(() => {
fn.apply(this, arguments)
}, wait)
}
}
view.onscroll =debounce(function () {
let sh = view.clientHeight;
let st = view.scrollTop;
let ch = img.offsetTop;
ch - st < sh && (img.src = img.dataset.src)
})
getBoundingClientReat
方法计算Element.getBoundingClientReat()
方法返回元素的大小及其相对于视口的位置。
该方法返回一个DOMRect
对象,对象包括left
, top
, right
, bottom
, x
, y
, width
, 和 height
这8个属性值。除了width
和 height
以外的属性是相对于视图窗口的左上角来计算的。
Element.getBoundingClientRect() - Web API 接口参考 | MDN (mozilla.org)
以滚动计算距离列举的代码例子来说,可视窗口为浏览器窗口,对img
dom对象使用getBoundingClientReat()
获取top,top即为浏览器窗口左上角到img
标签左上角的像素距离点
利用上述点,当top
值小于浏览器窗口高度时,说明img
已滚动于可视区域,即进行加载
#view {
width: 300px;
height: 500px;
margin: 30px auto;
overflow-y: auto;
}
.parent {
height: 1000px;
}
img {
margin-top: 800px;
margin-bottom: 30px;
}
<div id="view">
<img alt="..." class="img" data-src="https://p.pstatp.com/origin/pgc-image/c206105e0c46482a85b69a67a59ce682" />
div>
let img = document.getElementsByClassName('img')[0];
let view = document.getElementById('view');
//省略防抖代码
window.onscroll = debounce(() => {
let clientHeigth = view.clientHeight;
let { top } = img.getBoundingClientRect();
top < clientHeigth && (img.src = img.dataset.src);
})
view.onscroll =debounce(function () {
let sh = view.clientHeight;
let { top } = img.getBoundingClientRect();
top<sh && (img.src = img.dataset.src)
})
IntersectionObserver
监听IntersectionObserver
API原理与2. getBoundingClientReat
方法计算一样,官方认为图片懒加载,开发者既要获取top值,又要监听滚动事件,还要进行防抖处理,一系列操作麻烦不方便。为了简化它,官方提供了IntersectionObserver
接口。
IntersectionObserver(callback[, options])
当监听的元素达到要求后,触发callback
回调函数,它接收两个参数
IntersectionObserver API 使用教程 - 阮一峰的网络日志 (ruanyifeng.com)
let img = document.getElementsByClassName('img')[0];
let view = document.getElementById(view);
let ob = new IntersectionObserver(function (changes) {
changes.forEach(val => {
let { isIntersecting, target } = val;
isIntersecting && (target.src = target.dataset.src);
})
}, {
root: view,
threshold: [0]
})
ob.observe(img);
标签属性loading
兼容性差,不推荐
标签属性
loding
,可选值:
eager
:立即加载图像
lazy
:延迟加载图像,直到它和视口接近到一个计算得到的距离,由浏览器定义
详细介绍:https://developer.mozilla.org/zh-CN/docs/Web/HTML/Element/img#attr-loading
<div class="parent">
<img loading="lazy"
src="https://tse1-mm.cn.bing.net/th?id=OIP-C.FaG6dzohGs3q45-DwsEyQQHaEK&w=165&h=100&c=8&rs=1&qlt=90&o=6&pid=3.1&rm=2" alt="...">
div>
期待各位同学指点补充!
觉得文章有用的同学点赞一波!