图片懒加载知识

图片懒加载是为了减少资源的负担,在加载首屏图片时减少浏览器负担,加快首屏加载速度。之前在加载图片列表的时候总是图省事直接用element-ui的图片组件,今天好好的了解了图片懒加载的原理,具体如下:

一、懒加载原理

  1. 判断图片是否进入可视区域范围内
  2. 图片进入可视区域后触发图片加载
  3. 在判断图片是否进入可视区域范围内主要是判断可视区域的高度以及图片距离可视区域顶部的高度。
  4. 当前可视区域的高度 - 元素距离可视区域顶部的高度 > 0 说明图片进入可视区域,此刻出发加载数据事件

二、原生代码

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>图片懒加载</title>
    <style>
        .lazy-img-list {
            display: flex;
            align-items: center;
            flex-wrap: wrap;
            justify-content: space-between;
        }

        .lazy-img-list img {
            width: 49%;
            height: 180px;
            margin-bottom: 10px;
        }
    </style>
</head>
<body>
    <div class="lazy-img-list">
        <img src="./img/1.jpg" alt="" data-src="./img/1.jpg">
        <img src="./img/1.jpg" alt="" data-src="./img/2.jpg">
        <img src="./img/1.jpg" alt="" data-src="./img/3.jpg">
        <img src="./img/1.jpg" alt="" data-src="./img/4.jpg">
        <img src="./img/1.jpg" alt="" data-src="./img/2.jpg">
        <img src="./img/1.jpg" alt="" data-src="./img/3.jpg">
        <img src="./img/1.jpg" alt="" data-src="./img/4.jpg">
        <img src="./img/1.jpg" alt="" data-src="./img/2.jpg">
        <img src="./img/1.jpg" alt="" data-src="./img/3.jpg">
        <img src="./img/1.jpg" alt="" data-src="./img/4.jpg">
        <img src="./img/1.jpg" alt="" data-src="./img/2.jpg">
        <img src="./img/1.jpg" alt="" data-src="./img/3.jpg">
        <img src="./img/1.jpg" alt="" data-src="./img/1.jpg">
        <img src="./img/1.jpg" alt="" data-src="./img/2.jpg">
        <img src="./img/1.jpg" alt="" data-src="./img/3.jpg">
        <img src="./img/1.jpg" alt="" data-src="./img/4.jpg">
        <img src="./img/1.jpg" alt="" data-src="./img/2.jpg">
        <img src="./img/1.jpg" alt="" data-src="./img/3.jpg">
        <img src="./img/1.jpg" alt="" data-src="./img/4.jpg">
        <img src="./img/1.jpg" alt="" data-src="./img/2.jpg">
        <img src="./img/1.jpg" alt="" data-src="./img/3.jpg">
    </div>
    <script>
        const imgs = document.getElementsByTagName('img')
        // 获取可视区域的高度
        const viewHeight = document.documentElement.clientHeight
        // 统计当前加载到哪张图片,避免每一次都从第一张照片开始检查
        let num = 0
        // 当前可视区域的高度 - 元素距离可视区域顶部的高度 > 0 说明元素进入可视区域
        function lazyLoad() {
            for (let i = 0; i < imgs.length; i++) {
                const item = imgs[i]
                let distance = viewHeight - item.getBoundingClientRect().top
                if (distance > 0) {
                    item.src = item.getAttribute("data-src")
                    num = i + 1
                }
            }
        }
        // 节流函数
        function throttle(fn, delay) {
            let timer
            let prevTime
            return function (...args) {
                const currTime = Date.now()
                const context = this
                if (!prevTime) prevTime = currTime
                clearTimeout(timer)

                if (currTime - prevTime > delay) {
                    prevTime = currTime
                    fn.apply(context, args)
                    clearTimeout(timer)
                    return
                }
                timer = setTimeout(function () {
                    prevTime = Date.now()
                    timer = null
                    fn.apply(context, args)
                }, delay)
            }
        }
        // 监听scroll事件
        window.addEventListener("scroll", throttle(lazyLoad,200))
        lazyLoad()
    </script>
</body>
</html>

三、IntersectionObserver的使用

  1. 原理
    IntersectionObserver 是一个新的API,可以自动监听元素是否进入了设备的可视区域,而不需要频繁的计算去做判断。对于图片懒加载来说,需要获取可视化区域高度计算高度差才可以来触发图片加载。而IntersectionObserver这个方法可直接判断(观察)。
    IntersectionObserver用法
  2. 使用场景
    1)图片懒加载——图片滚动到可见时再加载
    2)内容无限滚动——用户滚动到接近内容底部时直接加载更多,无需用户操作翻页,给用户一种网页可以无限滚动的错觉。
    3)检测广告的曝光情况——为了计算广告收益,需要知道广告元素的曝光情况
    4)在用户看见某个区域时执行任务或播放动画
  3. 具体使用方法
const target = document.getElementById("dom")
        const io = new IntersectionObserver((changes, observer)=>{
            console.log('changes-------',changes);
            console.log('observer-------',observer);
        })
        // observe方法
        // 开始观察 —— 用来指定被监听的目标元素
        io.observe(target)
        // 停止观察 —— 指定停止监听的目标元素
        io.unobserve(target)
        // 关闭观察 —— 不需要接收参数
        io.disconnect()
        // 返回所有被观察到对象,返回值是一个数组
        const observerList = io.takeRecords()
        console.log('被观察对象------',observerList);
  1. 用IntersectionObserver实现图片懒加载
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Intersection Observer用法</title>
    <style>
        .lazy-img-list {
            display: flex;
            align-items: center;
            flex-wrap: wrap;
            justify-content: space-between;
        }
        .lazy-img-list img {
            width: 49%;
            height: 180px;
            margin-bottom: 10px;
        }
    </style>
</head>
<body>
    <div id="dom" class="lazy-img-list">
        <img src="./img/1.jpg" alt="" data-src="./img/1.jpg">
        <img src="./img/1.jpg" alt="" data-src="./img/2.jpg">
        <img src="./img/1.jpg" alt="" data-src="./img/3.jpg">
        <img src="./img/1.jpg" alt="" data-src="./img/4.jpg">
        <img src="./img/1.jpg" alt="" data-src="./img/2.jpg">
        <img src="./img/1.jpg" alt="" data-src="./img/3.jpg">
        <img src="./img/1.jpg" alt="" data-src="./img/4.jpg">
        <img src="./img/1.jpg" alt="" data-src="./img/2.jpg">
        <img src="./img/1.jpg" alt="" data-src="./img/3.jpg">
        <img src="./img/1.jpg" alt="" data-src="./img/4.jpg">
        <img src="./img/1.jpg" alt="" data-src="./img/2.jpg">
        <img src="./img/1.jpg" alt="" data-src="./img/3.jpg">
        <img src="./img/1.jpg" alt="" data-src="./img/1.jpg">
        <img src="./img/1.jpg" alt="" data-src="./img/2.jpg">
        <img src="./img/1.jpg" alt="" data-src="./img/3.jpg">
        <img src="./img/1.jpg" alt="" data-src="./img/4.jpg">
        <img src="./img/1.jpg" alt="" data-src="./img/2.jpg">
        <img src="./img/1.jpg" alt="" data-src="./img/3.jpg">
        <img src="./img/1.jpg" alt="" data-src="./img/4.jpg">
        <img src="./img/1.jpg" alt="" data-src="./img/2.jpg">
        <img src="./img/1.jpg" alt="" data-src="./img/3.jpg">
    </div>
    <script>
        // 图片懒加载
        // 获取图片列表
        const imgs = document.querySelectorAll('img[data-src]')
        const config = {
            rootMargin: '0px',
            threshold: 0
        }
        let observer = new IntersectionObserver((entries,self) => {
            entries.forEach((entry)=>{
                if(entry.isIntersecting) {
                    let img = entry.target
                    let src = img.dataset.src
                    if(src) {
                        img.src = src
                        img.removeAttribute('data-src')
                    }
                    // 解除观察
                    self.unobserve(entry.target)
                }
            })
        },config)
        imgs.forEach((image) => {
            // 开始观察
            observer.observe(image)
        })
    </script>
</body>
</html>
  1. 在vue中实现图片懒加载
    1) 首先先写一个页面,展示图片列表
<template>
  <div class="lazy-div">
    <p>图片加载</p>
    <div class="lazy-img-list">
      <img v-for="(item,index) in imgList"  :key="index" v-lazy="item.src">
    </div>
  </div>
</template>

<script>
export default {
  name: 'LazyLoad',
  data() {
    return {
      imgList: [
        {
          key: 1,
          src: require('../../assets/images/1.jpg')
        },
        {
          key: 2,
          src: require('../../assets/images/2.jpg')
        },
        {
          key: 3,
          src: require('../../assets/images/3.jpg')
        },
        {
          key: 4,
          src: require('../../assets/images/4.jpg')
        },
        {
          key: 1,
          src: require('../../assets/images/1.jpg')
        },
        {
          key: 2,
          src: require('../../assets/images/2.jpg')
        },
        {
          key: 3,
          src: require('../../assets/images/3.jpg')
        },
        {
          key: 4,
          src: require('../../assets/images/4.jpg')
        },
        {
          key: 1,
          src: require('../../assets/images/1.jpg')
        },
        {
          key: 2,
          src: require('../../assets/images/2.jpg')
        },
        {
          key: 3,
          src: require('../../assets/images/3.jpg')
        },
        {
          key: 4,
          src: require('../../assets/images/4.jpg')
        },
        {
          key: 1,
          src: require('../../assets/images/1.jpg')
        },
        {
          key: 2,
          src: require('../../assets/images/2.jpg')
        },
        {
          key: 3,
          src: require('../../assets/images/3.jpg')
        },
        {
          key: 4,
          src: require('../../assets/images/4.jpg')
        },
        {
          key: 1,
          src: require('../../assets/images/1.jpg')
        },
        {
          key: 2,
          src: require('../../assets/images/2.jpg')
        },
        {
          key: 3,
          src: require('../../assets/images/3.jpg')
        },
        {
          key: 4,
          src: require('../../assets/images/4.jpg')
        }
      ]
    }
  }
}
</script>
<style scoped>
.lazy-img-list {
    display: flex;
    align-items: center;
    flex-wrap: wrap;
    justify-content: space-between;
}
.lazy-img-list img {
    width: 49%;
    height: 180px;
    margin-bottom: 10px;
}
</style>

2) 创建一个LazyLoad.js,暴露出一个install方法,并在main.js 引入,便于在vue组件中使用

// LazyLoad.js
const LazyLoad = {
  // install 方法
  install(Vue, options) {
    const defaultSrc = options.default
    Vue.directive('lazy', {
      bind(el, binding) {
        LazyLoad.init(el, binding.value, defaultSrc)
      },
      inserted(el) {
        if (IntersectionObserver) {
          LazyLoad.observe(el)
        } else {
          LazyLoad.listenerScroll(el)
        }
      }
    })
  },
  // 初始化
  init(el, val, def) {
    el.setAttribute('data-src', val)
    el.setAttribute('src', def)
  },
  // 利用IntersectionObserver监听el
  observe(el) {
    var io = new IntersectionObserver((entries) => {
      const realSrc = el.dataset.src
      if (entries[0].isIntersecting) {
        if (realSrc) {
          el.src = realSrc
          el.removeAttribute('data-src')
        }
      }
    })
    io.observe(el)
  },
  // 监听scroll事件
  listenerScroll(el) {
    const handler = LazyLoad.throttle(LazyLoad.load, 300)
    LazyLoad.load(el)
    window.addEventListener('scroll',()=>{
      handler(el)
    })
  },
  // 加载真实图片
  load(el) {
    const windowHeight = document.documentElement.clientHeight
    const elTop = el.getBoundingClientRect().top
    const elBtm = el.getBoundingClientRect().bottom
    const realSrc = el.dataset.src
    if (elTop - windowHeight < 0 && elBtm > 0) {
      if (realSrc) {
        el.src = realSrc
        el.removeAttribute('data-src')
      }
    }
  },
  // 节流
  throttle(fn, delay) {
    let timer
    let prevTime
    return function(...args) {
      const currTime = Date.now()
      const context = this
      if (!prevTime) prevTime = currTime
      clearTimeout(timer)
      if (currTime - prevTime > delay) {
        prevTime = currTime
        fn.apply(context, args)
        clearTimeout(timer)
        return
      }
      timer = setTimeout(function () {
        prevTime = Date.now()
        timer = null
        fn.apply(context, args)
      }, delay)
    }
  }
}
export default LazyLoad
// main.js(根据自己组件的实际位置引入)
import LazyLoad from "../src/views/lazyLoad/LazyLoad"
Vue.use(LazyLoad)
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值