IntersectionObserver实现无线滚动和懒加载

最近发现一个有意思的Web API IntersectionObserver 和大家分享一下

IntersectionObserver 可以用于检测元素是否进入视口,可以用于实现无限滚动、懒加载等功能。

使用场景:在Web应用中,可能需要实现无限滚动、懒加载等功能,使用IntersectionObserver可以方便地实现这些功能。
IntersectionObserver的教学 大家可以参考mdn,或者看看阮一峰的博客讲解的比较细致。http://www.ruanyifeng.com/blog/2016/11/intersectionobserver_api.html

我大概讲一下IntersectionObserver的用法

API简介

var observer = new IntersectionObserver(callback,options);
let target = document.querySelector('#listItem');
observer.observe(target); // 开始观察
observer.unobserve(target);  // 停止观察
observer.disconnect(); // 关闭观察器

IntersectionObserver支持两个参数:

callback是当被监听元素的可见性变化时,触发的回调函数
options是一个配置参数,可选,有默认的属性值

callback

目标元素的可见性变化时,就会调用观察器的回调函数callback。

let callback =(entries, observer) => {
  entries.forEach(entry => {
    // Each entry describes an intersection change for one observed target element:
    // entry.boundingClientRect
    // entry.intersectionRatio
    // entry.intersectionRect
    // entry.isIntersecting
    // entry.rootBounds
    // entry.target
    // entry.time
  });
};
IntersectionObserverEntry 对象
{
  time: 3893.92, // 可见性发生变化的时间,是一个高精度时间戳,单位为毫秒
  rootBounds: ClientRect { // 根元素的矩形区域的信息
    bottom: 920,
    height: 1024,
    left: 0,
    right: 1024,
    top: 0,
    width: 920
  },
  boundingClientRect: ClientRect { // 目标元素的矩形区域的信息
     // ...
  },
  intersectionRect: ClientRect {  // 目标元素与视口(或根元素)的交叉区域的信息
    // ...
  },
  intersectionRatio: 0.54, // 目标元素的可见比例
  target: element  // 被观察的目标元素,是一个 DOM 节点对象
}
Option 对象

一、threshold 属性
threshold属性决定了什么时候触发回调函数。它是一个数组,每个成员都是一个门槛值,默认为[0],即交叉比例(intersectionRatio)达到0时触发回调函数。
二、root 属性,rootMargin 属性
root属性指定目标元素所在的容器节点
rootMargin属性根元素的margin,用来扩展或缩小rootBounds这个矩形的大小,从而影响intersectionRect交叉区域的大小

let options = {
  root: document.querySelector('#scrollArea'),
  rootMargin: '0px',
  threshold: 1.0
}

let observer = new IntersectionObserver(callback, options);

案例一 懒加载

<script setup lang="ts">
import { ref, onMounted, onUnmounted } from "vue";
// loading-image
const imgUrl = ref(new URL("/src/assets/loading.png", import.meta.url).href);
let observer: IntersectionObserver;
onMounted(() => {
  // 注册观察者
  observer = new IntersectionObserver((entries) => {
    for (let index = 0; index < entries.length; index++) {
      // 获取到目标元素img标签
      const target = entries[index].target as HTMLImageElement;
      // 观察者返回的对象
      const element = entries[index];
      if (element.isIntersecting) {
        target.src = target.dataset.src ?? "";
        observer && observer.unobserve(target);
      }
    }
  });

  // 遍历所有class为lazy-image的图片
  const imgs: HTMLCollection = document.getElementsByClassName("lazy-image");
  for (const img of Array.from(imgs)) {
    img && observer.observe(img);
  }
  // 断开所有观察
  onUnmounted(() => {
    observer.disconnect();
  });
});
</script>
 
<template>
  <div class="card" v-for="item in 10" :key="item">
    <img
      class="lazy-image"
      data-src="http://xxxx/image_1685348257589.png"
      :src="imgUrl"
      alt="某网站logo"
    />
  </div>
</template>
 
<style scoped>
.read-the-docs {
  color: #888;
}
.card {
  padding: 200px;
}
</style>

查看mdnIntersectionObserverEntry新增了isIntersecting属性

返回一个布尔值,如果目标元素与交叉区域观察者对象 (intersection observer) 的根相交,则返回 true .如果返回 true, 则 IntersectionObserverEntry 描述了变换到交叉时的状态; 如果返回 false, 那么可以由此判断,变换是从交叉状态到非交叉状态。

案例二 无限加载

<script setup lang="ts">
import FooterVue from "@/pages/my/components/Footer.vue";
import { ref, onMounted, onUnmounted, reactive } from "vue";
// loading-image
const imgUrl = ref(new URL("/src/assets/loading.png", import.meta.url).href);
let observer: IntersectionObserver;
onMounted(() => {
  // 注册观察者
  var intersectionObserver = new IntersectionObserver(function (entries) {
    console.log("entries: ", entries);
    // 如果不可见,就返回
    if (entries[0].intersectionRatio <= 0) return;
    loadItems(10);
    console.log("Loaded new items");
  });

  // 开始观察
  const foot = document.querySelector(".scrollerFooter");
  if (foot !== null) {
    intersectionObserver.observe(foot);
  }
  // 断开所有观察
  onUnmounted(() => {
    observer.disconnect();
  });
});
const loadItems = (num: number) => {
  for (let i = 0; i < 10; i++) {
    pageData.list.push(i);
  }
};
let pageData = reactive({
  list: [1, 2, 3, 4, 5, 6, 7, 8, 9, 10],
});
</script>
 
<template>
  <div class="card" v-for="(item, index) in pageData.list" :key="index">
    <div class="item">{{ item }}</div>
  </div>
  <div class="scrollerFooter item"></div>
</template>
 
<style scoped>
.item {
  height: 80px;
  border: 1px solid #f0f0f0;
  margin: 20px;
}
</style>
  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
IntersectionObserver是一种新的API,可以用于实现懒加载懒加载是指在页面滚动时,只加载可视区域内的内容,而不是一次性加载所有内容。这样可以优化网页加载速度,提升用户体验。 下面是IntersectionObserver实现懒加载的步骤: 1. 创建一个IntersectionObserver对象:可以通过其构造函数创建。 2. 选取需要进行懒加载的图片或其他DOM元素,将其包裹在一个容器元素中。 3. 使用IntersectionObserver对象的observe方法来观察容器元素。 4. 在回调函数中获取到可见性变化的元素,如果元素进入可视区域,就将其src属性指向真实图片的地址,实现图片的懒加载。 下面是一个示例代码: ``` let lazyImages = document.querySelectorAll('.lazy-image'); let options = { root: null, // 窗口视图作为根元素 rootMargin: '0px', // 不添加边框 threshold: 0.1 // 当可见性达到10%时触发回调 }; let observer = new IntersectionObserver(function(entries, observer) { entries.forEach(entry => { if (entry.isIntersecting) { // 元素进入可见视图 let lazyImage = entry.target; lazyImage.src = lazyImage.dataset.src; observer.unobserve(lazyImage); // 取消观察 } }); }, options); lazyImages.forEach(lazyImage => { observer.observe(lazyImage); }); ``` 在上面的代码中,我们首先选取了所有带有lazy-image类的图片元素,然后创建了一个IntersectionObserver对象,指定了观察的根元素和触发回调的阈值。接着,我们遍历了所有图片元素,使用observe方法观察了每个元素。在回调函数中,我们判断元素是否进入了可视区域,如果是,就将该元素的data-src属性值赋给src属性,实现懒加载,然后取消对该元素的观察。最后,我们通过forEach方法将所有图片元素添加到IntersectionObserver的观察列表中。 注意,容器元素的宽高需要在CSS中指定,否则IntersectionObserver无法准确计算元素的可见性。同样,图片元素的真实地址需要通过data-src属性指定,而不是src属性。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值