目前图片懒加载的方式主要有两种:

  1、利用getBoundingClientRectAPI得到当前元素与视窗的距离来判断

  2、利用h5的新API IntersectionObserver 来实现

getBoundingClientRect

  Element.getBoundingClientRect() 方法返回值是一个 DOMRect 对象,包含了该元素一组矩形的集合:是与该元素相关的css边框集合(top, left, right, bottom)。

我们可以采用如下方法来判断是否在可视区域:

  1. isViewport (el) {
  2. const viewWidth = window.innerWidth || document.documentElement.clientWidth;
  3. const viewHeight = window.innerHeight || document.documentElement.clientHeight;
  4. let { top, left, right, bottom } = el.getBoundingClientRect()
  5. return (
  6. top >= 0 &&
  7. left >= 0 &&
  8. right <= viewWidth &&
  9. bottom <= viewHeight
  10. )
  11. }

getBoundingClientRect 方式来实现需要监听 scroll 方法来配合,对于浏览器的性能会有一定的问题。

IntersectionObserver

  而 IntersectionObserver 方法是在2016年初提出来的,该API提供了一种异步观察目标元素相对与 root 元素是否进入了可视区域。作为一个新兴API,会有一定的兼容问题,点击查看兼容性
  当 IntersectionObserver 对象被创建,其被配置为监听根中一段给定比例的可见区域。一旦 IntersectionObserver 被创建,则无法更改其配置,所以一个给定的观察者对象只能用来监听可见区域的特定变化值,但是可以在同一个观察者对象中配置监听多个目标元素。
 

 属性

  root: 所监听对象的具体祖先元素。如果未传入值或值为null,则默认使用顶级文档的视窗。

  rootMargin: 计算交叉时添加到根(root)边界盒bounding box的矩形偏移量, 可以有效的缩小或扩大根的判定范围从而满足计算需要。

  thresholds: 可以是一个单独的number,也可以是一个number数组。当 root 元素与 target 元素相交达到该值的时候会执行回调。当值设定为0时,那么 target 元素有一个像素出现在 root 元素,回调就会执行;如果值设为1,那么就是当 target 元素完全出现在 root 元素当中才会执行。默认为0。如果当值设定为 [0, 0.25, 0.5, 0.75, 1] 时,那么每满足一个值就会回调一次。该值设定的时候不是复数,当取值的时候为复数:

  1. var observer = new IntersectionObserver(_observer, {
  2. root : null,
  3. threshold: [] // 单数
  4. });
  5.  
  6. observer.thresholds // 复数

 方法

  IntersectionObserver.disconnect: 停止所有监听工作

  IntersectionObserver.observe: 开始监听一个目标元素

  IntersectionObserver.takeRecords: 返回所有观察目标对象的数组

  IntersectionObserver.unobserve: 停止监听特定目标元素。

 
下面添上图片懒加载 js 代码:
  1. function LazyLoad (config) {
  2. this.default = {
  3. root: null,
  4. threshold: 0
  5. }
  6. this.settings = Object.assign(this.default, config)
  7. this.images = []
  8. this.observer = null
  9. this.init()
  10. }
  11.  
  12. LazyLoad.prototype = {
  13. init () {
  14. if (!window.IntersectionObserver) {
  15. this.loadImages()
  16. return
  17. }
  18.  
  19. this.images = document.querySelectorAll(this.settings.selector || '[data-src]')
  20.  
  21. let _this = this
  22. let observeConfig = {
  23. root: this.settings.root,
  24. rootMargin: this.settings.rootMargin,
  25. threshold: [this.settings.threshold]
  26. }
  27.  
  28. this.observer = new IntersectionObserver(changes => {
  29. Array.prototype.forEach.call(changes, entry => {
  30.  
  31. if (entry.isIntersecting) {
  32.  
  33. let target = entry.target
  34. _this.observer.unobserve(target)
  35. let src = target.dataset.src
  36.  
  37. if (target.tagName.toLowerCase() === 'img') {
  38. target.src = src
  39. } else {
  40. target.style.backgroundImage = `url(${src})`
  41. }
  42.  
  43. target.removeAttribute('data-src')
  44. }
  45. })
  46. }, observeConfig)
  47.  
  48. Array.prototype.forEach.call(this.images, image => {
  49. _this.observer.observe(image)
  50. image.src = _this.settings.placeholder
  51. })
  52.  
  53. },
  54.  
  55. loadImages () {
  56. let _this = this
  57. _this.replaceSrc()
  58.  
  59. let hasDone = false
  60. function _scroll() {
  61. if (hasDone) return
  62. hasDone = true
  63. setTimeout(() => {
  64. _this.replaceSrc()
  65. hasDone = false
  66. }, 100)
  67. }
  68. window.onscroll = _scroll
  69. },
  70.  
  71. replaceSrc () {
  72. let _this = this
  73. let imgs = document.querySelectorAll(this.settings.selector || '[data-src]')
  74. Array.prototype.forEach.call(imgs, image => {
  75. if (!image.src) image.src = _this.settings.placeholder
  76. let src = image.dataset.src
  77. if (_this.isInnerView(image)) {
  78. if (image.tagName.toLowerCase() === 'img') {
  79. image.src = src
  80. } else {
  81. image.style.backgroundImage = `url(${src})`
  82. }
  83. image.removeAttribute('data-src')
  84. }
  85. })
  86. },
  87.  
  88. isInnerView (el) {
  89. const viewWidth = window.innerWidth || document.documentElement.clientWidth;
  90. const viewHeight = window.innerHeight || document.documentElement.clientHeight;
  91. let { top, left, right, bottom } = el.getBoundingClientRect()
  92. return (
  93. top >= 0 &&
  94. left >= 0 &&
  95. right <= viewWidth &&
  96. bottom <= viewHeight
  97. )
  98. },
  99.  
  100. destroy () {
  101. this.observer.disconnect();
  102. }
  103. }
 
 
 

原生js - 两种图片懒加载实现原理的更多相关文章

  1. js可视区域图片懒加载

    可视区域图片懒加载 实现原理,页面滚动时获取需要懒加载的图片,判断此图片是否在可视区域内,是则设置图片data-src地址为src地址,加载图片. html下载地址 <!DOCTYPE html ...

  2. js实现网页图片延时加载的原理和代码 提高网站打开速度

    有时我们看到一些大型网站,页面如果有很多图片的时候,当你滚动到相应的行时,当前行的图片才即时加载的,这样子的话页面在打开只加可视区域的图片,而其它隐藏的图片则不加载,一定程序上加快了页面加载的速度,对 ...

  3. 前端实现图片懒加载(lazyload)的两种方式

    在实际的项目开发中,我们通常会遇见这样的场景:一个页面有很多图片,而首屏出现的图片大概就一两张,那么我们还要一次性把所有图片都加载出来吗?显然这是愚蠢的,不仅影响页面渲染速度,还浪费带宽.这也就是们通 ...

  4. JS图片懒加载

    简介 当页面图片太多时,加载速度就会很慢.尤其是用2G/3G/4G访问页面,不仅页面慢,而且还会用掉很多流量.图片懒加载的原理就是将页面内所有需要加载的图片全部换成一张默认的图片(一般尺寸很小),只有 ...

  5. 图片懒加载--lazyload.js的用法

    这几天公司的项目已经完成的差不多了,只剩下各种优化问题.今天着重于图片加载的优化.当一个页面需要下拉很长而且又有过多的图片要加载时,就会发生很多http请求,就会拉慢网页加载速度,用户体验不友好.怎么 ...

  6. Vue图片懒加载

    图片懒加载的原理 先将img标签中的src链接设为同一张图片(空白图片),将其真正的图片地址存储再img标签的自定义属性中(比如data-src).当js监听到该图片元素进入可视窗口时,即将自定义属性 ...

  7. 使用jQuery实现图片懒加载原理

    原文:https://www.liaoxuefeng.com/article/00151045553343934ba3bb4ed684623b1bf00488231d88d000 在网页中,常常需要用 ...

  8. 页面性能优化-原生JS实现图片懒加载

    在项目开发中,我们往往会遇到一个页面需要加载很多图片的情况.我们可以一次性加载全部的图片,但是考虑到用户有可能只浏览部分图片.所以我们需要对图片加载进行优化,只加载浏览器窗口内的图片,当用户滚动时,再 ...

  9. 原生js开发,无依赖、轻量级的现代浏览器图片懒加载插件,适合在移动端开发使用

    优势 1.原生js开发,不依赖任何框架或库 2.支持将各种宽高不一致的图片,自动剪切成默认图片的宽高 比如说你的默认图片是一张正方形的图片,则各种宽度高度不一样的图片,自动剪切成正方形. 完美解决移动 ...

随机推荐

  1. 问题 |无法找到Python路径,需手动配置环境变量

    问题: 在命令行cmd输入Python,如果出现以下无法识别命令行的报错,说明在系统环境变量中无法找到对应之前安装的Python的路径,则需手动配置一下 怎么配置? 1.打开我的电脑——右键——属性— ...

  2. flex: 1在ios10.2系统手机端的换行布局失败问题

    使用flex:1要追加flex-basis: auto;可以简写flex: 1 1 auto; 表格不可以用flex布局

  3. 分布式项目spring 配置文件的约束

    <?xml version="1.0" encoding="UTF-8"?><beans xmlns="http://www.spr ...

  4. Java分支结构

    Java 分支结构 - if...else/switch 顺序结构只能顺序执行,不能进行判断和选择,因此需要分支结构. Java有两种分支结构: if语句 switch语句 if语句 一个if语句包含 ...

  5. vue基础四

    1.绑定Html Class(在 v-bind 用于 class 和 style 时, Vue.js 专门增强了它.表达式的结果类型除了字符串之外,还可以是对象或数组) 1.1对象语法 传给v-bin ...

  6. spark面试问题收集

    spark面试问题 1.spark中的RDD是什么,有哪些特性 RDD(Resilient Distributed Dataset)叫做弹性分布式数据集,是Spark中最基本的数据抽象,它代表一个不可 ...

  7. 2019 wannafly winter camp day5-8代码库

    目录 day5 5H div2 Nested Tree (树形dp) 5F div2 Kropki (状压dp) 5J div1 Special Judge (计算几何) 5I div1 Sortin ...

  8. cgo 和 Go 语言是两码事

    cgo不是Go 借用 JWZ的一句话 有些人,当他们面临一个问题时,认为“我知道,我会使用 cgo ”.那么现在,他们有了两个问题. 最近有人在 Gopher 的 Slack Channel 上使用 ...

  9. python中的缓存技术

    python缓存技术 def console(a,b): print('进入函数') return (a,b) print(console(3,'a')) print(console(2,'b')) ...

  10. PHP面试 PHP基础知识 九(面向对象)

    面向对象 PHP的类权限控制修饰符 public(公共的) . protected(受保护的).private(私有的) public :最高权限   可以在类的内部使用  可以在类的外部使用  可以 ...