likes
comments
collection
share

JavaScript 实现图片懒加载的三种常用方法

作者站长头像
站长
· 阅读数 14

前言

1.1 什么是使用图片懒加载?

懒加载是一种对网页性能优化的方式,比如当访问一个网页的时候,优先显示可视区域的图片而不是一次加载全部的图片,当需要显示时,再发送请求加载图片。

1.2 为什么使用图片懒加载?

  1. 避免首次加载时消耗大量时间,降低页面渲染速度,造成卡顿现象。
  2. 按需加载,避免无效图片的加载,减轻服务器压力,节约网络资源。

若不使用图片懒加载,页面启动时,会加载全部的图片资源:

JavaScript 实现图片懒加载的三种常用方法

1.3 图片懒加载的实现原理

1 基本原理: 监听图片是否位于页面的可视区域内,若在则加载图片,不在则不加载图片

2 实现方案: 自定义属性-将图片真实地址 url 存储在自定义属性中,当监听到图片进入可视区域时,将自定义属性值赋值给 img 的 src 属性

实现方法

2.1 利用元素的 getBoundingClientRect 方法实现

(1)属性介绍:

利用.getBoundingClientRect实时获取物体的动态位置

JavaScript 实现图片懒加载的三种常用方法

(2)实现步骤:

步骤 1:监听页面滚动事件,lazyLoad为页面滚动时的处理函数,在本节为处理图片懒加载。

window.addEventListener('scroll', lazyLoad)

步骤 2:判断图片是否处于可视区域内

(1)若距离顶部top小于页面的整体高度window.innerHeight

(2)若距离左侧left小于页面的整体宽度window.innerWidth

(3)同时图片的底部bottom与图片的右侧right 距页面顶部、左侧的距离均大于0

则说明该图在屏幕的可视区域内。

JavaScript 实现图片懒加载的三种常用方法

为了提高复用性,我们可以将它封装成一个自定义函数isVisible,将每张图片作为参数传入该函数,并返回truefalse

// 可视区域判断函数
  function isVisible(img) {
    // 判断是否在可视区域,并返回true或false
    const imgRect = img.getBoundingClientRect() // getBoundingClientRect 获取图片的动态信息
    return imgRect.bottom > 0 && imgRect.top < window.innerHeight && imgRect.right > 0 && imgRect.left < window.innerWidth
  }

步骤 3:定义图片懒加载时的处理事件,监听所有的img,判断该img是否处于可视范围内

querySelectorAll 获取的元素为伪数组 需要转为真数组,否则无法使用数组的某些方法

// 获取所有的img元素,并利用扩展运算符转为真数组
const images = [...document.querySelectorAll('img')] 

JavaScript 实现图片懒加载的三种常用方法

步骤 4:对每张图片进行监听,利用自定义函数isVisible判断是否在可视区域内

(1)若处于可视区域:将自定义的data-src值,赋值给真正的src属性值,其中 data-src存储图片的URL地址,并删除该元素防止重复加载

(2)若不处于可视区域:return 不做处理

// 利用循环判断每张图片是否属于可视区域
function lazyLoad(){
    for (let i = 0; i < images.length; i++) {
    // isVisible是否该图片位于可视区域 返回true 或false
      if (isVisible(images[i])) {
        // 将元素的自定义属性 data-src 赋值给元素的 src 属性
        // 等价于:img.setAttribute('src', img.getAttribute('data-src'))
        images[i].src = images[i].dataset.src 
        // 防止重复被遍历 加载完之后 删除元素不再加载
        images.splice(i, 1)
        i--
      }
    }
}
lazyLoad()

(3)整体代码:

  // html 标签结构
  <img data-src="./public/image/VCG211430870249.jpg" src="./public/image/默认.jpg" alt="">
  <img data-src="./public/image/VCG211430987515.jpg" src="./public/image/默认.jpg" alt="">
  <img data-src="./public/image/VCG211431054751.jpg" src="./public/image/默认.jpg" alt="">
  <img data-src="./public/image/VCG211435102490.jpg" src="./public/image/默认.jpg" alt="">
  <img data-src="./public/image/VCG211438229829.jpg" src="./public/image/默认.jpg" alt="">
  <img data-src="./public/image/VCG211438109615.jpg" src="./public/image/默认.jpg" alt="">
// 1 获取全部图片的DOM节点
// 注意:querySelectorAll 值为伪数组利用扩展运算符转为真数组
const images = [...document.querySelectorAll('img')] 

// 2 监听页面滚动事件
window.addEventListener('scroll', lazyLoad)

// 3 定义页面滚动的处理函数
function lazyLoad(){
    for (let i = 0; i < images.length; i++) {
    // isVisible是否该图片位于可视区域 返回true 或false
      if (isVisible(images[i])) {
        // 将元素的自定义属性 data-src 赋值给元素的 src 属性 
        // dataset.src 此为元素的自定义属性 data-src
        images[i].src = images[i].dataset.src // 等价于:img.setAttribute('src', img.getAttribute('data-src'))
        // 防止重复被遍历 加载完之后 删除元素不再加载
        images.splice(i, 1)
        i--
      }
    }
}
lazyLoad()

// 4 可视区域判断函数
  function isVisible(img) {
    // 判断是否在可视区域
    const imgRect = img.getBoundingClientRect() // getBoundingClientRect 获取图片的动态信息
    return imgRect.bottom > 0 && imgRect.top < window.innerHeight && imgRect.right > 0 && imgRect.left < window.innerWidth
  }

2.2 利用整体距离实现

(1)属性介绍:

  • clientHeight : 网页可见区域高
    • A 表示可见区域的高度,包含padding 不包含 bordermargin
    • B 语法:element.clientHeight
    • C 备注:body.clientHeight = window.innerHeight
  • innertHeight : window 整体高度
    • A 表示window的内部高度,包括纵向滚动条
    • B 语法:window.innertHeight
  • offsetTop : 距离父级元素顶部的高度
    • A 表示当前元素相对于其offsetParent元素的顶部内边距的距离
    • B 语法:element.offsetTop
  • scrollTop : 网页被卷去的距离
    • A 表示在有滚动条时,滚动条向下滚动的距离也就是元素顶部被遮住部分的高度
    • B 语法:element.scrollTop

(2)实现步骤:

可以用image.offsetTop <= document.documentElement.clientHeight + document.documentElement.scrollTop 判断图片是否可以在可视区域内。

  • 图片元素位置的顶部距离:offsetTop

  • 滚动距离的最下端:scrollTop+clientHeight

  // html 标签结构
  <img data-src="./public/image/VCG211430870249.jpg" src="./public/image/默认.jpg" alt="">
  <img data-src="./public/image/VCG211430987515.jpg" src="./public/image/默认.jpg" alt="">
  <img data-src="./public/image/VCG211431054751.jpg" src="./public/image/默认.jpg" alt="">
  <img data-src="./public/image/VCG211435102490.jpg" src="./public/image/默认.jpg" alt="">
  <img data-src="./public/image/VCG211438229829.jpg" src="./public/image/默认.jpg" alt="">
  <img data-src="./public/image/VCG211438109615.jpg" src="./public/image/默认.jpg" alt="">
// 1 获取全部图片的DOM节点
// 注意:querySelectorAll 值为伪数组利用扩展运算符转为真数组
const images = [...document.querySelectorAll('img')] 

// 2 监听页面滚动事件
window.addEventListener('scroll', lazyLoad)

// 3 定义页面滚动的处理函数
function lazyload(e){
  // 3.1 获取屏幕的可视高度
  const clientHeight = document.documentElement.clientHeight
  // 3.2 获取屏幕的滚动距离
  const scrollTop = document.documentElement.scrollTop
  for (let i = 0; i < images.length; i++) {
    if (images[i].offsetTop < clientHeight + scrollTop) {
      images[i].setAttribute('src', images[i].getAttribute('data-src'))
    }
  }
}

2.3 利用Intersection Observer实现

Intersection Observer是一个比较新的api,他允许你追踪目标元素与其祖先元素或视窗的交叉状态,用他来检测图片是否进入视口非常方便,不用再像之前绑定事件、计算距离等。

(1)属性介绍:

  • 利用Intersection Observer实例上的observeunobserve方法,注册或取消监听事件。

  • 利用isIntersecting方法,判断该图片是否处于图片与屏幕可视区域的交叉范围内。

  • 注意:Intersection Observer实例会监听交叉状态,即出现和消失(触发两次),出现交叉状态后会去调用new的时候传入的callback回调函数

(2)实现步骤:

步骤 1: 监听页面滚动事件,lazyLoad为页面滚动时的处理函数,在本节为处理图片懒加载。

window.addEventListener('scroll', lazyLoad)

步骤 2: 创建图片与可视区域交叉实例

callback

  • 此为传入的回调函数,用于当处于交叉状态改变时进行的处理函数
  • 该函数会被触发2次:图片进入视野时+图片离开视野时
 const observer = new IntersectionObserver(callback)

步骤 3: 利用observer实例上的.observe(img)方法,给每张图片绑定观察事件

// 给每一个图片绑定观察方法
  imagess.forEach(img => {
    // 图片进入视野+离开视野时会触发callback回调函数
    observer.observe(img)
  })

步骤 4: 定义图片的懒加载事件

imgArr:

  • 可以获得包含所有图片的isIntersecting属性的集合,该属性可判断是否在交叉区域内
  • target为该图片的标签元素

JavaScript 实现图片懒加载的三种常用方法

  // callback 接收的参数为带有监听所有图片交叉属性的集合
  const callback = (imgArr) => {
    console.log('视图交叉时触发,离开交叉时也触发', imgArr) 
    imgArr.forEach(e => {
      // 判断是否在视野区域
      if (e.isIntersecting) {
        e.target.src = e.target.dataset.src
        // 取消监听,避免重复加载同一张图片
        observer.unobserve(e.target)
      }
    })
  }

(3)整体代码:

  // html 标签结构
  <img data-src="./public/image/VCG211430870249.jpg" src="./public/image/默认.jpg" alt="">
  <img data-src="./public/image/VCG211430987515.jpg" src="./public/image/默认.jpg" alt="">
  <img data-src="./public/image/VCG211431054751.jpg" src="./public/image/默认.jpg" alt="">
  <img data-src="./public/image/VCG211435102490.jpg" src="./public/image/默认.jpg" alt="">
  <img data-src="./public/image/VCG211438229829.jpg" src="./public/image/默认.jpg" alt="">
  <img data-src="./public/image/VCG211438109615.jpg" src="./public/image/默认.jpg" alt="">
  // intersectionObserver 交叉观察 : 目标元素和可视窗口会产生交叉区域
  const imagess = [...document.querySelectorAll('img')]

  // 2.1 创建视觉交叉的观察实例
  const observer = new IntersectionObserver(callback)
  // 2.2 给每一个图片绑定观察方法
  imagess.forEach(img => {
    // 2.3 图片进入视野+离开视野时触发 - 回调
    observer.observe(img)
  })

  // callback 接收的参数为带有监听所有图片交叉属性的集合
  const callback = (imgArr) => {
    console.log('视图交叉时触发,离开交叉时也触发', imgArr) // imgArr为
    imgArr.forEach(e => {
      // 判断是否在视野区域
      if (e.isIntersecting) {
        e.target.src = e.target.dataset.src
        // 取消观察追踪,避免重复加载同一张图片
        observer.unobserve(e.target)
      }
    })
  }

相关资料

浅析图片懒加载(三种实现方法与两种优化方式)_懒加载优化_钧桐的博客-CSDN博客

转载自:https://juejin.cn/post/7245613765692833849
评论
请登录