Logo
Published on

JavaScript实现懒加载图片、动画

Authors
  • avatar
    Name
    Monster Cone
    Twitter

懒加载是前端一种常见的优化手段,下面将介绍两种常见的懒加载实现

方法一 (getBoundingClientRect API)

该方法优点是兼容性较好,所有浏览器都支持。代码如下

const innerHeight = window.innerHeight
const debounce = (func, delay = 200) => {
  let timer
  return function (...args) {
    const context = this
    clearTimeout(timer)
    timer = setTimeout(() => {
      func.apply(context, args)
    }, delay)
  }
}
const lazyLoading = () => {
  let imgs = [...document.querySelectorAll('img')]
  imgs.forEach((el) => {
    const { top, height } = el.getBoundingClientRect()
    if (innerHeight - top > 0) {
      el.classList.add('animation')
      el.src = el.dataset.src
    }
    if (top + height < 0 || top > innerHeight) {
      el.classList.remove('animation')
    }
  })
}
window.addEventListener('scroll', debounce(lazyLoading, 300), false)
window.onload = lazyLoading

使用 getBoundingClientRect 获取元素高度和距离顶部的距离,计算是否在可视区内,如果在可视区内就给图片设置 src 属性或者添加动画 class,如果不在可视区内就移除动画 class,使元素在下次滚动到可视区时还能有动画效果。因为有些图片一开始就在顶部,这时候并不会触发 scroll 事件,就需要在页面加载完成时调用一次 lazyLoading 方法。

优化点:如果只需要使用图片懒加载,可以将选择器改为 document.querySelectorAll("img[src='']"),这样只会遍历还没加载图片的 img 元素。

方法二 (IntersectionObserver API)

IntersectionObserver 不支持 IE 浏览器,也不支持一些低版本的浏览器,但在今日,对于一些主流的浏览器兼容是足够的。它更容易实现。代码如下

let observer
const loadImage = (entries) => {
  entries.forEach((entry) => {
    if (entry.intersectionRatio > 0) {
      const { src } = entry.target.dataset
      entry.target.src = src
      entry.target.classList.add('animation')
    } else {
      entry.target.classList.remove('animation')
    }
  })
}
window.onload = () => {
  observer = new IntersectionObserver(loadImage)
  let imgs = [...document.querySelectorAll('img')]
  imgs.forEach((el) => {
    observer.observe(el)
  })
}

首先我们在页面加载完成时注册一个 IntersectionObserver 实例,同时获取所有的 img 元素并使用 IntersectionObserver 实例进行监听。callback 通常会在元素进入视口和离开视口时触发,如果你在实例化 IntersectionObserver 的时候配置了 threshold 属性,它也会在 threshold 条件下触发。

回调函数接受两个参数,一个 IntersectionObserverEntry 对象,一个触发 IntersectionObserver 的实例

IntersectionObserverEntry 中包含如下属性

  • boundingClientRect(触发元素宽高属性,同 getBoundingClientRect)
  • intersectionRatio(相交比例)
  • intersectionRect(相交区域)
  • isIntersecting(与根相交)
  • rootBounds(观察者根元素)
  • target(相交元素)
  • time(相交时间戳)

我们只需要 intersectionRatio 相交比例,在它大于 0 时我们设置 img 标签的 src 属性或者动画 class,在等于 0 时移除动画 class。

优化点:如果只需要使用图片懒加载,可以在设置了 src 属性后通过 observer.unobserve 方法移除元素的监听。

可以根据兼容性需要选择合适的实现方案