• 每天看一片代码系列(四):layzr.js,处理图片懒加载的库


    所谓图片的懒加载,即只有当图片处于或者接近于当前视窗时才开始加载图片。该库的使用方法非常简单:

    var layzr = new Layzr({ 
      attr: 'data-layzr',   // attr和retinaAttr必须至少有一个,用于指定对应的图片
      retinaAttr: 'data-layzr-retina', // 一般对应的图像比attr要高清
      threshold: 0,  // 距离视窗的距离为多少时开始加载
      callback: null  // 回调函数
    });

    代码解析

    首先是包装成为umd的方式:

    (function(root, factory) {
      if(typeof define === 'function' && define.amd) {
        define([], factory); // 使用amd定义一个模块,依赖为空
      } else if(typeof exports === 'object') {
        module.exports = factory(); // cmd方式,暴露返回值
      } else {
        root.Layzr = factory(); // 浏览器环境下
      }
    }(this, function() {
    
    
    })

    接下来是构造函数:

      function Layzr( options ) {
        this._lastScroll = 0;
        this._ticking = false;
     
        // 参数
        this._optionsAttr = options.attr || 'data-layzr';
        this._optionsAttrRetina = options.retinaAttr || 'data-layzr-retina';
        this._optionsThreshold = options.threshold || 0;
        this._optionsCallback = options.callback || null;
     
        // 获取合适的属性
        this._retina = window.devicePixelRatio > 1;
        this._imgAttr = this._retina ? this._optionsAttrRetina : this._optionsAttr;
     
        // 所有的图像集合
        this._images = document.getElementsByTagName('img');
     
        // call to create
        this._create();
      }

    create和destory函数:

    Layzr.prototype._create = function() {
        // 记录初始的scroll位置
        this._requestScroll();
     
        // scroll和resize对应的事件处理函数
        window.addEventListener('scroll', this._requestScroll.bind(this), false);
        window.addEventListener('resize', this._requestScroll.bind(this), false);
      }
     
      Layzr.prototype._destroy = function() {
        // unbind事件
        window.removeEventListener('scroll', this._requestScroll.bind(this), false);
        window.removeEventListener('resize', this._requestScroll.bind(this), false);
      }

    requestScroll的具体实现:

      Layzr.prototype._requestScroll = function() {
        this._lastScroll = window.scrollY || window.pageYOffset; // 垂直方向上的滚动距离
        this._requestTick();
      }
     
      Layzr.prototype._requestTick = function() {
        if(!this._ticking) {
         // requestAnimationFrame主要用于绘制图像,通过优化提高效率
         // 这里用于每次滚动都调用update
          requestAnimationFrame(this.update.bind(this));
          this._ticking = true;
        }
      }
    
      Layzr.prototype.update = function() {
        var imagesLength = this._images.length;
        for(var i = 0; i < imagesLength; i++) {
          var image = this._images[i];
     
         // 如果当前的图片有设定的属性
          if(image.hasAttribute(this._imgAttr) || image.hasAttribute(this._optionsAttr)) {
            // 且已经处于视窗中
            if(this._inViewport(image)) {
              // 加载这个图片
              this.reveal(image);
            }
          }
        }
     
        // allow for more animation frames
        this._ticking = false;
      }

    是否在视窗中的判断:

      Layzr.prototype._inViewport = function( imageNode ) {
        // 视窗的顶部和底部
        var viewportTop = this._lastScroll;
        var viewportBottom = viewportTop + window.innerHeight;
     
        // 图像的顶部和底部
        var elementTop = this._getOffset(imageNode);
        var elementBottom = elementTop + imageNode.offsetHeight;
     
        // 计算threshold对应的像素
        var threshold = (this._optionsThreshold / 100) * window.innerHeight;
     
        // 是否在这个区间中
        return elementBottom >= viewportTop - threshold && elementBottom <= viewportBottom + threshold;
      }

    展示图像的实现:

    Layzr.prototype.reveal = function( imageNode ) {
        // 获取图像的src
        var source = imageNode.getAttribute(this._imgAttr) || imageNode.getAttribute(this._optionsAttr);
     
        // 去除设置的属性
        imageNode.removeAttribute(this._optionsAttr);
        imageNode.removeAttribute(this._optionsAttrRetina);
     
        //设置src
        if(source) {
          imageNode.setAttribute('src', source);
     
          // 调用callback
          if(typeof this._optionsCallback === 'function') {
            this._optionsCallback.call(imageNode);
          }
        }
      }

    总结

    1. 基本流程: 滚动--》记录位置--》遍历图片--》判断是否在视窗中--》从属性中获取并设置图像src--》调用回调函数
    2. window.scrollY || window.pageYOffset 用于获取垂直滚动的距离
    3. 视窗高度:window.innerHeight,元素高度: node.offsetHeight
    4. 获取元素相对于doucment顶部的距离:http://stackoverflow.com/questions/5598743/finding-elements-position-relative-to-the-document
  • 相关阅读:
    vue学习03 v-html
    [spring guides]网关入门
    记一次公司mssql server密码频繁被改的事件
    重构 maixpy 的 board_info + config.json 从而自适应硬件版型。
    介绍 MaixUI 系列(一)如何食用?
    (旧文)在 micropython / esp-at / arduino 中实现 软串口(software-serial) 的参考
    以优化 MaixPy 的启动速度为例,说说 K210 的双核使用及原子操作。
    我是如何在 Win + VSCode 上开发 Keil for GD32 实现 I2C 从机的游戏机手柄。
    为 MaixPy 加入软 SPI 接口(移植 MicroPython 的 SPI)
    为 MaixPy 加入软 I2C 接口(移植 MicroPython 的 I2C)
  • 原文地址:https://www.cnblogs.com/cubika/p/4442988.html
Copyright © 2020-2023  润新知