angular监听图片加载完成_懒加载竟如此简单

a8da72a788b11d664350a9b748bd8d92.gif

IntersectionObserver接口(从属于Intersection Observer API)为开发者提供了一种可以异步监听目标元素与其祖先或视窗(viewport)交叉状态的手段。祖先元素与视窗(viewport)被称为根(root)。

好官方的描述!!!

不用管它,简单点说,IntersectionObserver就是观察一个元素是否在当前窗口中。

使用方法

/**  * @param {Function} callback 必填项,监听元素进入窗口时的回调函数  * @param {Object} options 非必填项,配置参数  * @param {Dom Element} options.root 监听元素的根元素,默认为全文档  * @param {String} options.rootMargin 元素到根元素边缘时的矩形偏移量,支持 px 和 %  * @param {Array} options.thresholds 交叉区域与边界区域的比例 */ var io = new IntersectionObserver(callback, options); io.observe(document.querySelector('img')); // 开始观察,接受一个DOM节点对象 io.unobserve(element); // 停止观察 接受一个element元素 io.disconnect(); // 关闭观察器

懵了么?还好,API的方法和属性比较少,先来撸一个案例你就明白怎么用了。

案例

<div id='app'>   <img src='./images/01.png'>   <img src='./images/01.png' >   <img src='./images/01.png' >   <img src='./images/01.png' >   <img src='./images/01.png' >   <img src='./images/01.png' > div> <script>   // 创建IntersectionObserver实例   const io = new IntersectionObserver(function (entrys) {     console.log(entrys);   }, {threshold: [1]});   function update () {     const els = document.querySelectorAll('img');     els.forEach(ele => {       io.observe(ele); // 对元素进行监听     });   }   update(); script>

此时,控制台会打印出监听的列表,注意列表中的每个元素都是一个监听器,其中target属性就是监听的元素,isIntersecting就是该元素是否在窗口内的标识。

图片懒加载

首先要明确我们的懒加载要有什么内容?

  • 1、默认占位图

  • 2、img元素上具有真实地址属性,如src

  • 3、我们刚刚看到的options.thresholds,即交叉区域与边界区域的比例

class InterObser {   constructor (attr = 'src', defaultImg, options = {}) {     this.attr = attr;     this.defaultImg = defaultImg;     this.options = options;     this.install();     this.update();   }   install () {     const that = this;     // 初始化 IntersectionObserver 实例     that.io = new IntersectionObserver(function (entrys) {       // 当监听到元素进入窗口时,循环每个监听器       entrys.forEach(ele => {         // 如果该监听器进入窗口,则将真实地址替换占位图         if (ele.isIntersecting) {           that.imgLoad(ele);           // 停止对该监听器的监听,防止多次操作           that.io.unobserve(ele.target);         }       });     }, that.options);   }   // 图片加载   imgLoad (ele) {     const url = ele.target.dataset.src;     ele.target.src = url;   }   update () {     const els = document.querySelectorAll(`[${this.attr}]`);     // 为每个监听元素设置默认值     els.forEach(ele => {       ele.src = this.defaultImg;       // 开始监听       this.io.observe(ele);     });   } } // 调用 new InterObser('src', './images/01.png', {threshold: [1]});

自建vue懒加载插件

既然我们知道了用IntersectionObserver做图片懒加载,那么我们是否可以将该功能做成插件呢?我目前的项目主要以vue为主,那么我就将其做成vue插件。

src目录下创建lib/index.js文件。

class InterObser {   install (Vue, options) {     const that = this;     // 创建指令,使用方法是v-lazy='http://xxx'     Vue.directive('lazy', {       bind(el, binding) {         // 为元素添加默认占位图         that.init(el, binding.value, options.defaultSrc);         // 创建IntersectionObserver实例         that.initObserve(options);       },       inserted (el, binding) {         that.observe(el, binding); // 对该元素开启监听       }     })   }   init (el, val, def) {     // 将v-lazy的值挂载到该元素上,因为编译后元素上并没有v-lazy属性     el.setAttribute('src', val)     // 设置默认占位符     el.src = def   }   initObserve (options) {     // 该只需创建一次     var io = new IntersectionObserver(function (entrys) {       // 循环每个监听的元素,当该元素进入窗口,则替换真实地址,并取消监听       entrys.forEach(entry => {         if (entry.isIntersecting) { // 当isIntersecting为true,则代表该元素进入了窗口           const el = entry.target;           el.src = el.dataset.src; // 用真实地址替换占位符           el.removeAttribute('src')); // 移除多余的src属性           setTimeout(function () {             io.unobserve(el); // 取消监听           }, 0);         }       })     }, options.interOptions || {});     this.io = io;   }   observe (el) {     this.io.observe(el);   } } export default new InterObser();

使用

main.js中进行配置

import Vue from 'vue'; import LazyLoad from './lib/index.js'; // 全局配置 Vue.use(LazyLoad, {   defaultSrc: 'http://localhost:3000/01.jpg',   // IntersectionObserver原生配置项   interOptions: {     threshold: [1]   } });

在组件中使用

<template>   <div>     <img v-lazy='lazySrc'>     <img v-lazy='lazySrc'>     <img v-lazy='lazySrc'>     <img v-lazy='lazySrc'>     <img v-lazy='lazySrc'>   div> template> <script> export default {   data () {     return {       lazySrc: 'http://localhost:3000/02.jpg'     };   } }; script>

是不是非常简单,我们来看看传统的图片懒加载是怎么实现的。

传统的图片懒加载

传统图片懒加载主要是依赖监听scroll和获取getBoundingClientRect().topgetBoundingClientRect().bottom来做的,但是这里涉及到性能问题,比如getBoundingClientRect会引起回流等,我们能够做的就是减少这些性能上的操作。

我们可以将上述插件做一下优化,当浏览器不支持IntersectionObserver时,我们就采用传统懒加载的方式。

class InterObser {   install () {     .....   }   initObserve (options) {     // 多了一个判断条件而已     // 仅当浏览器支持该API时才初始化     if (IntersectionObserver) {       var io = new IntersectionObserver(function () {         .......       }, options.interOptions || {});       this.io = io;     }   }   observe (el) {     // 如果浏览器支持IntersectionObserver,则使用,否则使用传统懒加载方式     if (IntersectionObserver) {       this.io.observe(el);     } else {       this.listenerScroll(el);     }   }   listenerScroll (el) {     const that = this;     that.load(el); // 为了使当前窗口的图片能够正常加载     window.addEventListener('scroll', function (e) {       // 防止多次触发       // 减少getBoundingClientRect操作从而达到优化的目的       if (el.dataset.src) {         that.load(el);       }     });   }   load (el) {     // 获取窗口高度     var docHeight = document.documentElement.clientHeight;     var boundingClientRect = el.getBoundingClientRect();     var bottom = boundingClientRect.bottom;     var top = boundingClientRect.top;     // 当元素进入窗口时,才加载真实图片     if (top < docHeight && bottom > 0) {       el.src = el.dataset.src;       el.removeAttribute('src');     }   } }

遗留问题

这里还遗留几个问题

  • 当用户传入了options.interOptions时没有做兼容处理

  • 除了判断el.dataset.src进行优化之外还可配合节流函数

  • 默认图片和v-lazy中的图片只能是网络图片

这几个问题后期慢慢补充,个人觉得可以完善这个插件,并且不止限于img标签,还可以对video等进行懒加载。


f313e224bcb13a8c1c456de74644dc28.png

(长按上图,弹出“识别二维码”后可快速关注)

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值