lazy-load-img.js?

1. 什么鬼?

一个轻量级的图片懒加载,我个人很是喜欢。

2. 有什么优势?

1.原生js开发,不依赖任何框架或库

2.支持将各种宽高不一致的图片,自动剪切成默认图片的宽高

       比如说你的默认图片是一张正方形的图片,则各种宽度高度不一样的图片,自动剪切成正方形

   完美解决移动端开发中,用户上传图片宽高不一致而导致的图片变形的问题。

3. 使用姿势,如下:

            // 生成li
var ul = document.querySelector('#list');
for (var i = 1; i <= 21; i++) {
var li = document.createElement('li');
li.innerHTML = `<img src="./images/default.png" data-src="./images/${i}.jpg">`;
ul.appendChild(li);
}; var lazyLoadImg = new LazyLoadImg({
el: ul, // dom元素下的图片
mode: 'diy', // 模式: 默认/自定义
time: 300, // 多长时间重新监听一次
complete: true, // 完成后自己销毁程序
position: { // 只要其中一个位置符合条件,都会触发加载机制
top: 0, // 元素距离顶部
left: 0, // 元素距离右边
right: 0, // 元素距离下面
bottom: 0 // 元素距离左边
},
diy: { //设置图片剪切规则,diy模式时才有效果
backgroundSize: 'cover',
backgroundRepeat: 'no-repeat',
backgroundPosition: 'center center'
},
before: function() { },
success: function(el) {
el.classList.add('success');
},
error: function(el) {
el.src = './images/error.png';
}
}); // lazyLoadImg.start() // 开启懒加载程序
// lazyLoadImg.destroy() // 销毁图片懒加载程序
// lazyLoadImg.restart() // 销毁后又可以重新开启懒加载程序

源码笔记(以下是我改版过的,因为原作者的代码兼容性做的很好,然而现在都使用es6+语法了,所以就...)

/**
* Created by Sorrow.X on 2017/4/27.
*/ ;(function(exports) {
class LazyLoadImg {
constructor() {
var options = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : {};
this.options = { // 实例的option属性(默认)
el: document.querySelector('body'), // 选择的元素
mode: 'default', // 默认模式,将显示原图,diy模式,将自定义剪切,默认剪切居中部分
time: 300, // 设置一个检测时间间隔
done: true, // 页面内所有数据图片加载完成后,是否自己销毁程序,true默认销毁,false不销毁:FALSE应用场景:页面异步不断获取数据的情况下 需要实时监听则不销毁
diy: { // 此属性,只有在设置diy 模式时才生效
backgroundSize: 'cover',
backgroundRepeat: 'no-repeat',
backgroundPosition: 'center center'
},
position: { // 只要其中一个位置符合条件,都会触发加载机制
top: 0, // 元素距离顶部
right: 0, // 元素距离右边
bottom: 0, // 元素距离下面
left: 0 // 元素距离左边
},
before: function before(el) {// 图片加载之前,执行钩子函数 },
success: function success(el) {// 图片加载成功,执行钩子函数 },
error: function error(el) {// 图片加载失败,执行的钩子函数 }
};
Object.assign({}, this.options, options);
Object.assign({}, this.options.diy, options.diy);
Object.assign(this.options, options); // 裁切图片用的
this.canvas = document.createElement('canvas');
this.canvas.getContext('2d').globalAlpha = 0.0;
this.images = {}; this._timer = true; // 给实例添加一个_timer属性(定时器)
this.start(); // 开启懒加载程序
} _testMeet(el) { // 每个dom元素,一般img元素
var options = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : {}; // position对象 // 取得元素在可视区的位置(相对浏览器视窗)左右上下
var bcr = el.getBoundingClientRect();
// padding+border+width
var mw = el.offsetWidth; // 元素自身宽度
var mh = el.offsetHeight; // 元素自身的高度
// 包含了导航栏
var w = window.innerWidth; // 视窗的宽度
var h = window.innerHeight; // 视窗的高度 var boolX = !(bcr.right - options.left <= 0 && bcr.left + mw - options.left <= 0) && !(bcr.left + options.right >= w && bcr.right + options.right >= mw + w); // 左右符合条件
var boolY = !(bcr.bottom - options.top <= 0 && bcr.top + mh - options.top <= 0) && !(bcr.top + options.bottom >= h && bcr.bottom + options.bottom >= mh + h); // 上下符合条件
return el.width !== 0 && el.height !== 0 && boolX && boolY;
} _getTransparent(src, w, h) {
if (this.images[src]) return this.images[src];
this.canvas.width = w;
this.canvas.height = h;
var data = this.canvas.toDataURL('image/png');
this.images[src] = data;
return data;
} start() {
var self = this; // LazyLoadImg实例存一下 var options = this.options; // 配置存一下 clearTimeout(this._timer); // 清除定时器
if (!this._timer) return;
// this._timer 是setTimeout的return flag 推荐采用settimeout的方法,而不是setinterval
this._timer = setTimeout(function () {
var list = Array.prototype.slice.apply(options.el.querySelectorAll('[data-src]')); // 获取el下所有含有data-src属性的标签,且转成数组
// 如果list.length为0 且页面内图片已经加载完毕 清空setTimeout循环
if (!list.length && options.done) { // list有数据就不关闭定时器
clearTimeout(self._timer); // 有页面内的图片加载完成了,自己销毁程序
} else {
list.forEach(function (el) { // 遍历dom
// 如果该元素状态为空(dataset HTML5方法 设置、获取属性);并且检测该元素的位置
if (!el.dataset.LazyLoadImgState && self._testMeet(el, options.position)) {
self.loadImg(el); // 加载图片
};
});
};
// call it
self.start();
}, options.time);
} loadImg(el) {
var self = this; // 加载图片
var options = this.options; el.dataset.LazyLoadImgState = 'start';
// 执行加载之前做的事情
options.before.call(this, el);
var img = new window.Image();
// 这里是一个坑 dataset.src 实际取的值是 属性data-src data- 是HTML5 DOMStringMap对象
img.src = el.dataset.src; // 图片加载成功
img.addEventListener('load', function () {
if (options.mode === 'diy') {
el.src = self._getTransparent(el.src, el.width, el.height);
options.diy.backgroundImage = 'url(' + img.src + ')';
Object.assign(el.style, options.diy);
} else {
el.src = img.src;
};
delete el.dataset.src;
el.dataset.LazyLoadImgState = 'success';
return options.success.call(self, el);
}, false); // 图片加载失败
img.addEventListener('error', function () {
delete el.dataset.src;
el.dataset.LazyLoadImgState = 'error';
options.error.call(self, el);
}, false);
} destroy() {
// 解除事件绑定,return掉,不会自调用
delete this._timer;
} restart() {
this._timer = true;
this.start();
}
}; exports.LazyLoadImg = LazyLoadImg;
})(window);

当然,原作者狼族小狈的源码如下,棒极了,很是喜欢

(function (global, factory) {
typeof exports === 'object' && typeof module !== 'undefined' ? module.exports = factory() :
typeof define === 'function' && define.amd ? define(factory) : (global.LazyLoadImg = factory());
}(this, function() {
'use strict'; // 只要其中一个位置符合条件,都会触发加载机制
var testMeet = function (el) { // 每个dom元素,一般img元素
var options = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : {}; // position对象 // 取得元素在可视区的位置(相对浏览器视窗)左右上下
var bcr = el.getBoundingClientRect();
// padding+border+width
var mw = el.offsetWidth; // 元素自身宽度
var mh = el.offsetHeight; // 元素自身的高度
// 包含了导航栏
var w = window.innerWidth; // 视窗的宽度
var h = window.innerHeight; // 视窗的高度 var boolX = !(bcr.right - options.left <= 0 && bcr.left + mw - options.left <= 0) && !(bcr.left + options.right >= w && bcr.right + options.right >= mw + w); // 左右符合条件
var boolY = !(bcr.bottom - options.top <= 0 && bcr.top + mh - options.top <= 0) && !(bcr.top + options.bottom >= h && bcr.bottom + options.bottom >= mh + h); // 上下符合条件
return el.width !== 0 && el.height !== 0 && boolX && boolY;
}; var canvas = document.createElement('canvas');
canvas.getContext('2d').globalAlpha = 0.0;
var images = {}; var getTransparent = function (src, w, h) {
if (images[src]) return images[src];
canvas.width = w;
canvas.height = h;
var data = canvas.toDataURL('image/png');
images[src] = data;
return data;
}; // 检查instance是否是Constructor的实例
var classCallCheck = function (instance, Constructor) {
if (!(instance instanceof Constructor)) {
throw new TypeError("Cannot call a class as a function");
};
}; // 给构造函数的原型添加方法
var createClass = function () {
function defineProperties(target, props) {
for (var i = 0; i < props.length; i++) { // 遍历数组
var descriptor = props[i]; // 描述对象
descriptor.enumerable = descriptor.enumerable || false; // 可枚举, 默认不可枚举
descriptor.configurable = true; // 可配置
if ("value" in descriptor) descriptor.writable = true; // 如果有value, 则可写
Object.defineProperty(target, descriptor.key, descriptor); // 给原型添加属性, 有描述符
}
} return function (Constructor, protoProps, staticProps) {
if (protoProps) defineProperties(Constructor.prototype, protoProps); // 原型添加方法
if (staticProps) defineProperties(Constructor, staticProps); // 原型添加属性
return Constructor; // 返回构造函数
};
}(); // 一级浅拷贝
var _extends = Object.assign || function (target) {
for (var i = 1; i < arguments.length; i++) { // 从第二个参数开始
var source = arguments[i]; // 值 for (var key in source) {
if (Object.prototype.hasOwnProperty.call(source, key)) { // 一级浅拷贝
target[key] = source[key]; // 覆盖target的值
};
};
}; return target;
}; var _win = window; var LazyLoadImg = function () {
// 构造函数 初始化参数
function LazyLoadImg() {
var options = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : {}; //参数个数不为0, 就把第一个参数赋给option
classCallCheck(this, LazyLoadImg); // this: LazyLoadImg的实例, LazyLoadImg: 构造函数 this.options = { // 实例的option属性(默认)
el: document.querySelector('body'), // 选择的元素
mode: 'default', // 默认模式,将显示原图,diy模式,将自定义剪切,默认剪切居中部分
time: 300, // 设置一个检测时间间隔
done: true, // 页面内所有数据图片加载完成后,是否自己销毁程序,true默认销毁,false不销毁:FALSE应用场景:页面异步不断获取数据的情况下 需要实时监听则不销毁
diy: { // 此属性,只有在设置diy 模式时才生效
backgroundSize: 'cover',
backgroundRepeat: 'no-repeat',
backgroundPosition: 'center center'
},
position: { // 只要其中一个位置符合条件,都会触发加载机制
top: 0, // 元素距离顶部
right: 0, // 元素距离右边
bottom: 0, // 元素距离下面
left: 0 // 元素距离左边
},
before: function before(el) {// 图片加载之前,执行钩子函数 },
success: function success(el) {// 图片加载成功,执行钩子函数 },
error: function error(el) {// 图片加载失败,执行的钩子函数 }
};
// 一级浅拷贝 如果都有 则右面的值 option.position会覆盖this.options.position
options.position = _extends({}, this.options.position, options.position); // 设置position值
options.diy = _extends({}, this.options.diy, options.diy); // 设置diy值
_extends(this.options, options); // 组合一下options数据
this._timer = true; // 给实例添加一个_timer属性(定时器)
this.start(); // 开启懒加载程序
}; createClass(LazyLoadImg, [{
key: 'start',
value: function start() {
var _this = this; // LazyLoadImg实例存一下 var options = this.options; // 配置存一下 clearTimeout(this._timer); // 清除定时器
if (!this._timer) return;
// this._timer 是setTimeout的return flag 推荐采用settimeout的方法,而不是setinterval
this._timer = setTimeout(function () {
var list = Array.prototype.slice.apply(options.el.querySelectorAll('[data-src]')); // 获取el下所有含有data-src属性的标签,且转成数组
// 如果list.length为0 且页面内图片已经加载完毕 清空setTimeout循环
if (!list.length && options.done) { // list有数据就不关闭定时器
clearTimeout(_this._timer); // 有页面内的图片加载完成了,自己销毁程序
} else {
list.forEach(function (el) { // 遍历dom
// 如果该元素状态为空(dataset HTML5方法 设置、获取属性);并且检测该元素的位置
if (!el.dataset.LazyLoadImgState && testMeet(el, options.position)) {
_this.loadImg(el); // 加载图片
};
});
};
// call it
_this.start();
}, options.time);
}
}, {
key: 'loadImg',
value: function loadImg(el) {
var _this2 = this; // 加载图片
var options = this.options; el.dataset.LazyLoadImgState = 'start';
// 执行加载之前做的事情
options.before.call(this, el);
var img = new _win.Image();
// 这里是一个坑 dataset.src 实际取的值是 属性data-src data- 是HTML5 DOMStringMap对象
img.src = el.dataset.src; // 图片加载成功
img.addEventListener('load', function () {
if (options.mode === 'diy') {
el.src = getTransparent(el.src, el.width, el.height);
options.diy.backgroundImage = 'url(' + img.src + ')';
_extends(el.style, options.diy);
} else {
el.src = img.src;
};
delete el.dataset.src;
el.dataset.LazyLoadImgState = 'success';
return options.success.call(_this2, el);
}, false); // 图片加载失败
img.addEventListener('error', function () {
delete el.dataset.src;
el.dataset.LazyLoadImgState = 'error';
options.error.call(_this2, el);
}, false);
}
}, {
key: 'destroy',
value: function destroy() {
// 解除事件绑定
delete this._timer;
}
},{
key: 'restart',
value: function restart() {
// 重新开始自调用
this._timer = true;
this.start();
}
}]
);
return LazyLoadImg;
}(); return LazyLoadImg;
}));

屁话放到现在,还不如看demo来的直接,走你,

移动手机端点我demo

ps: 很不错的一个轻量级图片懒加载的库。

github: https://github.com/lzxb/lazy-load-img

lazy-load-img.js 源码 学习笔记及原理说明的更多相关文章

  1. AlloyTouch.js 源码 学习笔记及原理说明

    alloyTouch这个库其实可以做很多事的, 比较抽象, 需要我们用户好好的思考作者提供的实例属性和一些回调方法(touchStart, change, touchMove, pressMove, ...

  2. AlloyFinger.js 源码 学习笔记及原理说明

    此手势库利用了手机端touchstart, touchmove, touchend, touchcancel原生事件模拟出了 rotate  touchStart  multipointStart   ...

  3. Underscore.js 源码学习笔记(下)

    上接 Underscore.js 源码学习笔记(上) === 756 行开始 函数部分. var executeBound = function(sourceFunc, boundFunc, cont ...

  4. Underscore.js 源码学习笔记(上)

    版本 Underscore.js 1.9.1 一共 1693 行.注释我就删了,太长了… 整体是一个 (function() {...}());  这样的东西,我们应该知道这是一个 IIFE(立即执行 ...

  5. Vue.js 源码学习笔记

    最近饶有兴致的又把最新版 Vue.js 的源码学习了一下,觉得真心不错,个人觉得 Vue.js 的代码非常之优雅而且精辟,作者本身可能无 (bu) 意 (xie) 提及这些.那么,就让我来吧:) 程序 ...

  6. move.js 源码 学习笔记

    源码笔记: /* move.js * @author:flfwzgl https://github.com/flfwzgl * @copyright: MIT license * Sorrow.X - ...

  7. observe.js 源码 学习笔记

    /** * observejs --- By dnt http://kmdjs.github.io/ * Github: https://github.com/kmdjs/observejs * MI ...

  8. Vue.js 源码学习笔记 -- 分析前准备1 -- vue三大利器

    主体 实例方法归类:   先看个作者推荐, 清晰易懂的  23232 简易编译器   重点: 最简单的订阅者模式 // Observer class Observer { constructor (d ...

  9. Vue.js 源码学习笔记 - 细节

     1. this._eventsCount = { }    这是为了避免不必要的深度遍历: 在有广播事件到来时,如果当前 vm 的 _eventsCount 为 0, 则不必向其子 vm 继续传播该 ...

随机推荐

  1. Docker 启动遇到 Error starting daemon: Error initializing network controller 错误

    docker 版本 1.10.3 一台装有 docker 的机器重启后,没法启动,/var/log/messages 展示如下错误信息: May 17 11:11:14 gziba-hc03 syst ...

  2. 从零开始学习html(六)开始学习CSS,为网页添加样式

    一.认识CSS样式 <!DOCTYPE HTML> <html> <head> <meta http-equiv="Content-Type&quo ...

  3. (网页)readonly和disabled的区别(转)

    转自脚本之家: 标签的readonly和disabled属性的区别: 在表单元素中,readonly和disable有类似之处,因为它们都可以将一些表单元素设置为"不可用"状态,当 ...

  4. vmare连接远程服务器的问题

    测试环境:两端都是VMware Workstation 12 Pro 1.需要共享虚拟机 在虚拟机上点击右键 -> Manage -> Share 后面按照操作设置 2.远程服务器的443 ...

  5. WebAPi使用Autofac实现依赖注入

    WebAPi依赖注入  使用记录 笔记 1.NuGet包安装 2.控制器加入构造函数 3.Global.asax  ----Application_Start 应用程序启动时 using Autofa ...

  6. mysql 临时数据突然变大

    晚上收到紧报警,一台数据库服务器磁盘空间使用快速从50%使用率到80%.我们生产的数据库都磁盘是>2T 登录机器发现*.myd文件异常大 登入数据库查询进程 mysql>showproce ...

  7. orcale 把日期当做查询条件

    根据日期查询范围 精确到天 select * from table where to_char( time,'yyyy mm dd ' )  <=   '2000 01 01' select * ...

  8. Linux 下安装 Tomcat 出现拒绝访问的情况

    此外也无法调用 java -version 查看版本号 ./shutdown 时:提示找不到 JDK 的某个文件夹 ./startup 时:却启动正常 访问 8080 端口时,显示拒绝访问 解决方法: ...

  9. Mysql连接错误:Mysql Host is blocked because of many connection errors

    环境:linux,mysql5.5.31错误:Host is blocked because of many connection errors; unblock with 'mysqladmin f ...

  10. MongoDB修改与聚合二

    1.修改方法 一 语法 里面有三个大的语句:一个是查询条件:一个是修改字段:一个是其他参数(目前就有两个) db.table.update( 条件, 修改字段, 其他参数 ) update db1.t ...