el-image :src 默认加载空图片_IntersectionObserver 和图片懒加载

本文介绍了如何使用 IntersectionObserver API 实现图片懒加载,详细解释了该API的工作原理和配置选项,并提供了一个常规的懒加载实现方法。通过监听元素与视口的交叉状态,优化了性能并确保只有在必要的时候才加载图片。

3657ac7c8f354b2ea0c0297b2802d91a.png

IntersectionObserver 这个 API 平常可能听得比较少,caniuse 兼容性报告目前支持率是 90.12%,还不推荐用于大众化的场景中,但是它的能力和性能非常的好。

fd914418bcfb538a6d9021af4f14a0e1.png

关于 IntersectionObserver

d24003250a085cf8ba60d1a99cf7fa55.png
  • MDN IntersectionObserver
  • 阮一峰 IntersectionObserver API 使用教程

IntersectionObserver 接口 (从属于Intersection Observer API) 提供了一种异步观察目标元素与其祖先元素或顶级文档视窗(viewport)交叉状态的方法。祖先元素与视窗(viewport)被称为根(root)。由于可见(visible)的本质是,目标元素与视口产生一个交叉区,所以这个 API 叫做"交叉观察器"。

简单点说就是它可以观察 root(默认是视口)和目标元素的交叉情况,当交叉率是 0% 或者 10% 或者更多的时候,可以触发指定的回调。

当一个 IntersectionObserver 对象被创建时,其被配置为监听根中一段给定比例的可见区域。一旦 IntersectionObserver 被创建,则无法更改其配置,所以一个给定的观察者对象只能用来监听可见区域的特定变化值;然而,你可以在同一个观察者对象中配置监听多个目标元素。

使用

const observer = new IntersectionObserver(callback, observerConfig)

创建一个新的 IntersectionObserver 对象,当其监听到目标元素的可见部分穿过了一个或多个阈(thresholds)时,会执行指定的回调函数。

用法

function cb (entries) {
  console.log(entries)
  entries.forEach(entry => {
    const target = entry.target;
    console.log(target)
    console.log(entry)
  });
}

let observerConfig = {
  root: null,
  rootMargin: '0px',
  threshold: [0],
}

const observer = new IntersectionObserver(cb, observerConfig)
const box = document.getElementById('#box')
// 开始观察
observer.observe(box)
// 停止观察
observer.unobserve(box)
// 关闭观察器,observer 所有的观察都会停止
observer.disconnect();

entries 是一个监听目标的数组,每个成员都是一个 IntersectionObserverEntry 对象。

cb 回调函数在最初会调用一次,这次 entries 会是所有的观察目标对象,在滑动的时候会把可见性变化符合 threshold 的对象作为 entries 传进来。

初始化(全部被观察的)

e4cd44505b3c20c00bb77c8a989a8baa.png

滚动时(符合条件的)

05b3efdf44d84af04f6f0a67ceca45c0.png

由于是异步的操作,可以看到 start end 这两个同步操作执行之后,才会执行 cb

IntersectionObserverEntry

{
  boundingClientRect: DOMRectReadOnly {x: 8, y: 380, width: 300, height: 300, top: 380, …}
  intersectionRatio: 0.023333333432674408
  intersectionRect: DOMRectReadOnly {x: 8, y: 380, width: 300, height: 7, top: 380, …}
  isIntersecting: true
  rootBounds: DOMRectReadOnly {x: 0, y: 0, width: 1903, height: 387, top: 0, …}
  target: img.lazy
  time: 440149.8099999735
}

含义如下

  • time:可见性发生变化的时间,是一个高精度时间戳,单位为毫秒,返回一个记录从 IntersectionObserver 的时间原点(time origin)到交叉被触发的时间的时间戳(DOMHighResTimeStamp).
  • target:被观察的目标元素,是一个 DOM 节点对象
  • rootBounds:根元素的矩形区域的信息,getBoundingClientRect() 方法的返回值,如果没有根元素(即直接相对于视口滚动),则返回 null
  • boundingClientRect:目标元素的矩形区域的信息
  • intersectionRect:目标元素与视口(或根元素)的交叉区域的信息
  • intersectionRatio:目标元素的可见比例,即 intersectionRectboundingClientRect 的比例,完全可见时为 1,完全不可见时小于等于0
  • isIntersecting 是否交叉

配置

observerConfig.root 所监听对象的具体祖先元素(element)。如果未传入值或值为 null,则默认使用顶级文档的视窗。

observerConfig.rootMargin 计算交叉时添加到根(root)边界盒 bounding box 的矩形偏移量, 可以有效的缩小或扩大根的判定范围从而满足计算需要。所有的偏移量均可用像素(pixel)(px)或百分比(percentage)(%)来表达, 默认值为"0px 0px 0px 0px",用法和普通的 margin 一样(top、right、bottom、left)。

observerConfig.threshold 注意 MDN 文档中的 thresholds 是错误的,一个包含阈值的列表, 按升序排列, 列表中的每个阈值都是监听对象的交叉区域与边界区域的比率。当监听对象的任何阈值被越过时,都会生成一个通知(Notification)。如果构造器未传入值, 则默认值为0,也可以是 [0, 0.25, 0.5, 0.75, 1]

懒加载的常规实现

常规实现都会监听滚动事件,通过 el.getBoundingClientRect() 获取到当前元素与视口的位置关系来确定图片是否加载,在加载完成之后为了性能考虑,删除 src ,这样就可以避免重复的执行,要注意的是 getBoundingClientRect 会触发浏览器的回流

getBoundingClientRect 这个方法的支持度已经非常高,可以放心使用。

218dbc88cd1fffb57a55067c2d87363b.png

核心是下面这个判断,这个方法可以只加载和视口交叉的元素,不会加载视口上面或者视口下面的图片。

$img.dataset.src
  && $img.getBoundingClientRect().bottom >= 0
  && windowHeight > $img.getBoundingClientRect().top
  1. 如果不存在 src 则直接跳过(性能优化);
  2. 判断元素底部是否出现在视口中,出现则显示;
  3. 判断元素顶部是否出现在视口中,出现则显示;
document.addEventListener('DOMContentLoaded', function() {
  const imgs = document.querySelectorAll('.lazy')

  function lazyLoad() {
    const windowHeight = document.documentElement.clientHeight
    imgs.forEach(($img, i) => {
      // 重点是下面这个判断
      if ($img.dataset.src && $img.getBoundingClientRect().bottom >= 0 && windowHeight > $img.getBoundingClientRect().top) {
        $img.src = $img.dataset.src
        delete $img.dataset.src
      }
    })
  }

  lazyLoad()
  document.addEventListener('scroll', debounce(lazyLoad, 200))
})

function debounce(func, wait) {
  let timer = null

  return function(...args) {
    if (timer) clearTimeout(timer)
    timer = setTimeout(() => {
      func(...args)
    }, wait)
  }
}

感谢阅读,欢迎关注我的公众号 云影sky,带你解读前端技术。关注公众号可以拉你进讨论群,有任何问题都会回复。

62ee2c22ef3a5f7a7632c10051126c4a.png
在使用 Element Plus 时,如果需要实现图片预览的懒加载功能(Lazy Loading),可以通过其 `el-image` 组件提供的原生支持来完成。Element Plus 的 `el-image` 组件内置了懒加载机制,可以在图片滚动到可视区域时才进行加载,从而优化页面性能资源使用。 ### 基本实现方式 要启用懒加载,只需将 `lazy` 属性设置为 `true`,并配合 `scroll-container` 指定监听滚动的容器。如果未指定 `scroll-container`,则默认监听全局窗口的滚动事件。 ```html <template> <div style="height: 500px; overflow-y: scroll;" ref="scrollContainer"> <el-image v-for="(src, index) in imageList" :key="index" :src="src" lazy :scroll-container="scrollContainer" style="width: 100%; height: 300px;" ></el-image> </div> </template> <script> export default { data() { return { scrollContainer: null, imageList: [ 'https://example.com/image1.jpg', 'https://example.com/image2.jpg', 'https://example.com/image3.jpg' ] }; }, mounted() { this.scrollContainer = this.$refs.scrollContainer; } }; </script> ``` ### 进阶配置与优化 - **占位图与加载失败处理**:可以使用 `placeholder` `error` 插槽自定义加载加载失败时的显示内容。 ```html <el-image :src="imageUrl" lazy :scroll-container="scrollContainer" > <template #placeholder> <div>Loading...</div> </template> <template #error> <div>Image load failed</div> </template> </el-image> ``` - **延迟加载时机**:默认情况下,图片会在进入视口时立即加载。如果希望增加提前加载的距离,可以通过 `offset` 属性调整触发加载的距离阈值。 - **移动端优化**:对于移动端场景,建议结合 `IntersectionObserver` 或第三方库如 `lazysizes` 实现更精细的控制,以提升用户体验。 通过上述方法,可以有效地在 Element Plus 中实现图片预览的懒加载功能,从而提高页面加载速度用户体验[^1]。 ---
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符  | 博主筛选后可见
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值