运用 IntersectionObserver 达到实现图片懒加载的功能
1 前言
这篇文章的起源来自于最近的一个产品需求,相爱相杀的产品经理希望在移动端预览转码后的PDF、PPT等文件,一般而言转码后的PDF、PPT等都会生成大量的图片,这时就涉及到了图片懒加载
的实现。在原有的印象中,图片懒加载的实现都是通过监听scroll事件实现:
- 添加自定义属性:给img标签添加
data-src
,值为图片的url,同时不要设置src属性 - 判断目标元素与视口的交叉状态:通过获取元素的getBoundingClientRect属性的top值和页面的clientHeight进行对比,如果top值小于clientHeight,则说明元素出现在可视区域之内
- 设置真实的src:当元素出现在可视区域内时,将真实的图片地址赋值给目标元素的src属性
但是,今天在调研的过程中发现了IntersectionObserver
这个API完全贴合了我的需求(PS:是我太落后了🙈~)。下面开始进入今天的正题吧!
2 IntersectionObserver 简介
IntersectionObserver 接口 (从属于Intersection Observer API) 提供了一种异步观察目标元素与其祖先元素或顶级文档视窗 (viewport) 交叉状态的方法。祖先元素与视窗 (viewport) 被称为**根 (root)。——引用自MDN
下面这些情况都需要用到相交检测:
- 图片懒加载——当图片滚动到可见时才进行加载
- 内容无限滚动——也就是用户滚动到接近内容底部时直接加载更多,而无需用户操作翻页,给用户一种网页可以无限滚动的错觉
- 检测广告的曝光情况——为了计算广告收益,需要知道广告元素的曝光情况
- 在用户看见某个区域时执行任务或播放动画
具体的属性以及方法就不赘述了,请直接参考MDN文档
3 基础用法
// 回调函数
const callback =(entries, observer) => {
entries.forEach(entry => {
// entry.boundingClientRect
// entry.intersectionRatio
// entry.intersectionRect
// entry.isIntersecting
// entry.rootBounds
// entry.target
// entry.time
});
};
// 参数配置
const options = {
root: document.querySelector('#scrollArea'),
rootMargin: '0px',
threshold: 1.0
};
// 1. 实例化一个IntersectionObserver对象,并传入相应参数和回调函数
const observer = new IntersectionObserver(callback, options);
// 2. 指定一个目标元素进行观察
const target = document.querySelector('#listItem')
observer.observe(target);
// 3.销毁时,一定要取消观察对象,否则会一直监听
observer.unobserve(target);
以上提到的entry
下每个属性的具体函数,请查阅MDN
4 图片懒加载的实现
由于项目中我们统一的技术栈为Vue3 Tsx,所以下面采用tsx的写法来编写。
import { defineComponent, ref, toRefs, PropType, onMounted, onUnmounted } from 'vue';
import styles from './index.module.scss';
import defaultImage from '@/assets/images/defaultImage.png';
export default defineComponent({
props: {
// 图片集合
imagesList: {
type: Array as PropType<string[]>,
required: true,
},
},
setup(props) {
const { imagesList } = toRefs(props);
const observer = ref<any>(null);
const root = ref<HTMLElement | null>(null);
// 监视器的回调函数
const handlerObserve = (entries: any) => {
entries.forEach(({ isIntersecting, target }: any) => {
if (isIntersecting) {
const targetImg = target.children[0];
targetImg.src = targetImg.dataset.src;
// 修改过src属性之后,即可移除data-src属性并且取消监视
targetImg.removeAttribute('data-src');
observer.value.unobserve(target);
}
});
};
// 针对图片容器添加监听器
const addObserve = () => {
const list = document.querySelectorAll('.image-item-observe') as NodeListOf<Element>;
list.forEach((item: Element) => {
observer.value.observe(item);
});
};
// 初始化监视器
const initObserve = () => {
observer.value = new IntersectionObserver(handlerObserve, {
root: root.value,
rootMargin: '0px 0px 500px 0px', // 监视区向下拓展500px
});
addObserve();
};
onMounted(async () => {
initObserve();
});
onUnmounted(() => {
// 关闭观察器
observer.value.disconnect();
});
return () => (
<div ref={root} class={styles['image-list-container']}>
{
imagesList.value.map((item: string, index: number) => (
<div class={`${styles['image-item-wrapper']} image-item-observe`}>
{/* 设置一个默认的缺省图,避免在加载过程中出现白屏的现象 */}
<img
class={styles['image-item']}
src={defaultImage}
data-src={item}
key={index}
width="100%"
/>
</div>
))
}
</div>
);
},
});
具体效果如下图所示(200张图的加载,整体看起来还是👌的):
5 结语
第一次写文章,算是迈出了第一步,希望以后能够坚持下去。就我自身而言,写文章主要是为了记录在工作中遇到的问题,以便后期的回顾和查阅。不足之处,还请大家包涵,以后请多多指教。
这篇好文章是转载于:学新通技术网
- 版权申明: 本站部分内容来自互联网,仅供学习及演示用,请勿用于商业和其他非法用途。如果侵犯了您的权益请与我们联系,请提供相关证据及您的身份证明,我们将在收到邮件后48小时内删除。
- 本站站名: 学新通技术网
- 本文地址: /boutique/detail/tanefhb
系列文章
更多
-
photoshop保存的图片太大微信发不了怎么办
PHP中文网 06-15 -
Android 11 保存文件到外部存储,并分享文件
Luke 10-12 -
word里面弄一个表格后上面的标题会跑到下面怎么办
PHP中文网 06-20 -
《学习通》视频自动暂停处理方法
HelloWorld317 07-05 -
微信公众号没有声音提示怎么办
PHP中文网 03-31 -
photoshop扩展功能面板显示灰色怎么办
PHP中文网 06-14 -
excel下划线不显示怎么办
PHP中文网 06-23 -
怎样阻止微信小程序自动打开
PHP中文网 06-13 -
excel打印预览压线压字怎么办
PHP中文网 06-22 -
photoshop蒙版画笔没反应怎么办
PHP中文网 06-24