全部废墟上的全部,也都将于完毕之后重新开端。 —— 镜神寂
1. 前言
作为在摸已经很久的一名前端er,今日终于迈出了写文章的步伐,首要用于记载平常工作中或许学习中遇到的一些问题以及笔记,便利今后查阅,当然也期望能够协助更多可爱的前端er。其实从很早以前就有了这个主意,但是每一次都没有坚持下去,现在从头开端,期望为时未晚。
这篇文章的来源来自于最近的一个产品需求,相爱相杀的产品司理期望在移动端预览转码后的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. 结语
第一次写文章,算是迈出了第一步,期望今后能够坚持下去。就我自身而言,写文章首要是为了记载在工作中遇到的问题,以便后期的回忆和查阅。不足之处,还请大家包容,今后请多多指教。
6. 参阅资料
- 阮一峰-IntersectionObserver API 使用教程
- MDN-Intersection Observer
- 利用IntersectionObserver 分分钟完成图片懒加载