原生 JS 实现图片懒加载的思路
实现方案
-
1. 在 img 元素时,自定义一个属性 data-src,用于存放图片的地址
-
2. 获取屏幕可视区域的尺寸
-
3. 获取元素到窗口边缘的距离
-
4. 判断元素时候在可视区域内,在的话则 data-src 的值赋给 src;否则不执行其他操作
本质上:当图片在可视区域内时才会加载否则不加载;也可以给一个默认的图片占位。
需要的 api:
-
IntersectionObserver
它提供了一种异步观察目标元素与顶级文档 viewport 的交集中的变化的方法 -
window.requestIdleCallback()
方法将在浏览器的空闲时段内调用的函数排队。这使开发者能够在主事件循环上执行后台和低优先级工作,而不会影响延迟关键事件,比如动画和输入影响。
几个注意的细节:
-
提前加载,可以加 100px
-
滚动时只处理未加载的图片即可
-
函数节流
简单代码演示
判断是否是在可视区域的三种方式: 屏幕可视区域的高度+滚动条滚动距离>元素到文档顶部的距离,即document.documentElement.clientHeight+document.documentElement.scrollTop> element.offsetTop 使用getBoundingClientRect()获取元索大小和位置 IetersectionObserver自动观察元素是否在视口内
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8" /> <meta name="viewport" content="width=device-width, initial-scale=1.0" /> <meta http-equiv="X-UA-Compatible" content="ie=edge" /> <title>图片懒加载</title> <style> img { display: block; height: 450px; margin-bottom: 20px; } </style> </head> <body> <img data-src="./images/1.png" alt="" /> <img data-src="./images/2.png" alt="" /> <img data-src="./images/3.png" alt="" /> <img data-src="./images/4.png" alt="" /> <img data-src="./images/5.png" alt="" /> <img data-src="./images/6.png" alt="" /> </body> <script> var imgs = document.querySelectorAll("img"); // 节流函数,定时器版本 function throttle(func, wait) { let timer = null; return function (...args) { if (!timer) { func(...args); timer = setTimeout(() => { timer = null; }, wait); } }; } //方法1: H + S > offsetTop function lazyLoad1(imgs) { //offsetTop是元素与offsetParent的距离,循环获取直到页面顶部 function getTop(e) { var T = e.offsetTop; while ((e = e.offsetParent)) { T += e.offsetTop; } return T; } var H = document.documentElement.clientHeight; //获取可视区域高度 var S = document.documentElement.scrollTop || document.body.scrollTop; Array.from(imgs).forEach(function (img) { // +100 提前100个像素就开始加载 // 并且只处理没有src即没有加载过的图片 if (H + S + 100 > getTop(img) && !img.src) { img.src = img.dataset.src; } }); } const throttleLazyLoad1 = throttle(lazyLoad1, 200); // 方法2:el.getBoundingClientRect().top <= window.innerHeight function lazyLoad2(imgs) { function isIn(el) { var bound = el.getBoundingClientRect(); var clientHeight = window.innerHeight; return bound.top <= clientHeight + 100; } Array.from(imgs).forEach(function (img) { if (isIn(img) && !img.src) { img.src = img.dataset.src; } }); } const throttleLazyLoad2 = throttle(lazyLoad2, 200); // 滚轮事件监听 // window.onload = window.onscroll = function () { // throttleLazyLoad1(imgs); // // throttleLazyLoad2(imgs); // }; // 方法3:IntersectionObserver function lazyLoad3(imgs) { const io = new IntersectionObserver((ioes) => { ioes.forEach((ioe) => { const img = ioe.target; const intersectionRatio = ioe.intersectionRatio; if (intersectionRatio > 0 && intersectionRatio <= 1) { if (!img.src) { img.src = img.dataset.src; } } img.onload = img.onerror = () => io.unobserve(img); }); }); imgs.forEach((img) => io.observe(img)); } lazyLoad3(imgs); </script> </html>