资讯专栏INFORMATION COLUMN

图片加载相关技术

ZweiZhao / 519人阅读

摘要:本小节将讨论与图片加载时机相关的技术预加载与懒加载。同样,可将请求成功与失败的回调,作为图片预加载成功与失败的处理。

目前,出于性能与灵活性方面的考虑,我们都将一些小图片替换成矢量图或者字体。除了这些能被替换的小图之外,还有一些不得不使用位图的场景,如照片、背景等。对于这些位图,我们需要考虑它们在加载过程中的不同状态,而制定不同的表现方案。

图片加载过程中的状态,大致包括:

何时决定加载图片

加载中

加载结束,失败的处理

本文将对比加载过程中的各个状态表现的不同实现方案,并对方案的简易程度、兼容性、可扩展性进行分析。

决定加载图片的时机

一般情况,页面加载时图片资源作为多带带的请求向服务器获取。有时,由于图片的数量与大小的影响,这导致页面加载缓慢,而且加载了许多用户未触发的图片,也白白浪费了流量;有时,又需要保证用户浏览网页的流畅性,不得不预先加载好图片,等用户触发显示图片。

可见,正确的图片加载时机对提高用户体验有很大的帮助。本小节将讨论与图片加载时机相关的技术:预加载懒加载

预加载

预加载技术就是在用户触发图片显示之前,先将图片加载到本地。当用户触发图片显示的事件时,浏览器能够及时渲染出图片,保证了用户浏览网页的流畅性。那么,如何实现预加载呢?本文将介绍三种实现预加载的技术。

利用CSS实现预加载

该方法只需将欲加载的图片url写入某个隐藏元素的background属性中,让这张图片和CSS文件同时加载。当用户触发显示图片的事件时,再将图片插入到目标位置。

如下代码所示,这里用到CSS3的background多图片特性,只需一个隐藏元素即可预加载所有图片。

.nothing-1 {
    display: none;
    background: url("1.jpg"), url("2.jpg"), url("3.jpg"); 
}

(关于background多图片特性的兼容性:http://caniuse.com/#search=cs... )

该方法的缺点在于无法控制预加载的时机,只能是页面加载时一起加载图片,如果图片过多,会阻塞页面的load事件,延长页面加载时间。

只利用JavaScript实现预加载

第二种实现预加载的技术,是直接使用JavaScript新建Image对象,对其src属性赋值。

如下代码所示:

var image = new Image();
image.src = "...img url";
image.onload = function() { ... }
image.onerror = function () { ... }

还可以利用Image对象的load事件和error事件,完成图片预加载和失败之后的处理,如所有图片加载完成后的提示等。

利用Ajax实现预加载

最后这种方法则是直接发送ajax请求,获取图片资源。同样,可将ajax请求成功与失败的回调,作为图片预加载成功与失败的处理。

如下代码所示(仅针对webkit浏览器):

var xhr = new XMLHttpRequest();
xhr.onreadystatechange = function() {
  if (xhr.readyState == 4) { 
    if ((xhr.status >= 200 && xhr.status < 300) || xhr.status == 304) {
      ...
    }
  }
}
xhr.open("GET", "...img url");
xhr.send("");

Ajax请求方法无需多余的Image对象,而且获取的资源也不仅局限于图片

懒加载

懒加载的核心思想是根据用户需要加载图片资源,并非在渲染页面时就获取所有图片。如此,不仅可以减轻服务器的压力节省用户流量,还可以提高页面加载速度。

实现思路

首先,将页面中所有图片url写进img的"data-src"属性,并将默认的loading图片写入src,作为加载中的图片样式。


其次,是确定判断图片加载的时机,即当图片进入浏览器的可视区域中时。那么问题来了,如何判断某个元素进入可视区域呢?

以垂直方向为例,判断元素是否进入可视区,首先我们需要知道:

可视区域的高度:window.innerHeight(IE9以前不可用)|| document.documentElement.clientHeight (IE标准模式);

元素相对于可视区域顶端位置:element.getBoundingClientRect().top;

元素相对于可视区域底端位置:element.getBoundingClientRect().bottom;

判断是否进入可视区,即判断元素的顶部相对于可视区域顶端位置是否大于0,且又小于可视区域高度;或者,元素的底部相对于可视区域底端位置是否大于0,且又小于可视区域高度

上图中列出了元素进入可视区的所有情形,从上到下来看,第一个元素的top与bottom的值都小于0,不在可视区内;第二个元素的top小于0,而bottom却在0到clientHeight之间,已在可视区内,需要加载;第三个元素的top与bottom都满足条件,在可视区中;剩下的两个元素与头两元素情况一致。

等到元素进入可视区域,就将img元素的"data-src"属性赋值给"src"属性,并标记该img元素正在加载,不再做此处理。

以上过程伪代码如下:

function check() {
  imgs.forEach(function(img, index) {
    if (loadedList.indexOf(index) >= 0) {
      return;
    }
    if (!isInClient(container)) {
      return;
    }
    loadedList.push(index);
    loadImage(img);
  })
}

最后,将判断图片进入可视区域后加载的check函数,写入window的scroll和resize事件即可

优化思路

有时用户若只想查看排序较后的图片,会快速滚动滚动条,这种情况下快速滚动过的图片也没有必要加载了。

该优化的实现思路也很简单,延迟加载逻辑,计时器一到再判断元素是否还在可视区域,可将代码修改为如下:

function check() {
  imgs.forEach(function(img, index) {
    if (loadedList.indexOf(index) >= 0) {
      return;
    }
    if (!isInClient(container)) {
      return;
    }
    
    setTimeout(function() {
      if (!isInClient(container)) {
        return;
      }
      loadedList.push(index)
      loadImage(img)
    }, 1000);
  })
}
加载中状态

图片出于加载中的状态时,需要给用户“图片正在加载”的提示,图片加载完成后,将提示隐藏。下面,本文将给出加载中提示的两种实现方案。

方案一:利用img元素background

此方案的实现思路是:将“加载中”提示作为img的背景,当图片正在加载中时,页面显示的是背景;图片加载完成,则覆盖了背景显示出图片
关键代码:


.img {
    width: 500px;
    height: 300px;
    background: url("data:image/gif;base64,...") center no-repeat;
}

这里将“加载中”的动态图片作为img标签的background,并且转换base64编码,这样可以减少一次请求。

浏览器兼容情况见下表:

浏览器 Chrome 54 Safari 10.0 Firefox 50.0 IE 8
兼容性

兼容性没有问题,唯一的限制是需要设定.img的宽高。若事先无法得到图片的宽高,则可以在img标签外套一层

,由外层容器的background作为“加载中”提示,容器中的img可根据容器的宽或高设定其大小,关键代码如下:

img { height: 100%; }
方案二:利用img元素的load事件

img元素的load事件会在图片加载完成后触发,所以我们可以利用这一特点,进行加载中提示。
实现思路:首次渲染img元素时,将“加载中”提示作为img元素,真正的图片则用另一个Image对象加载。等到真正的图片加载完成,即触发了Image对象的load事件,则将img元素的src修改为真正图片的url

关键代码如下:

Array.from(document.getElementsByTagName("img")).forEach(img => {
    const image = new Image();
    image.src = img.dataset.src;
    image.onload = () => img.src = image.src;
})

这种方法需要js配合,无论是加载中的提示动画还是图片都可以原始尺寸展示,而不需要再设置。

兼容性也毫无问题:

浏览器 Chrome 54 Safari 10.0 Firefox 50.0 IE 8
兼容性

上面介绍的两种方案都能实现“加载中”提示,方案一只需要CSS即可,方案二需要js配合但灵活性更强。

加载中的动画不仅可以用图片,还可以用CSS3的动画特性,对于以上两种方案稍作修改也可以适配CSS3的加载中动画提示:

方案一:加载动画作为外层div的内容,但z-index比图片低,等图片加载完即可覆盖;

方案二:用js控制img和加载中动画的显示与隐藏即可,图片加载完隐藏加载中动画,显示图片,并可设置过渡动画增强体验;

加载结束

图片加载结束具有两种结果,要么加载成功,要么失败。加载成功,我们不需多做处理,而加载失败会出现难看的裂图提示,影响体验。

本文介绍两种方法,替换图片加载失败的裂图提示。

方案一::before与:after伪元素

img是可替换元素,即其表现的形式与内容是被外部资源控制。当img未加载时,属于它的:before与:after伪元素并未渲染,只有当图片加载失败时,这两个伪元素才会出现。

这里给出加载失败的样式例子,CSS关键样式如下:

.broken-image {
    width: 100%;
    position: relative;
    min-height: 50px;
}

.broken-image:before {
    content: " ";
    position: absolute;
    top: -10px;
    left: 0;
    height: calc(100% + 10px);
    width: 100%;
    background-color: rgb(230, 230, 230);
    border: 2px dotted rgb(200, 200, 200);
    border-radius: 5px;
}

.broken-image:after {
    content: "f127" " Broken Image ";
    display: block;
    font-size: 16px;
    font-style: normal;
    font-family: FontAwesome;
    color: rgb(100, 100, 100);
    position: absolute;
    top: 5px;
    left: 0;
    width: 100%;
    text-align: center;
    line-height: 2;
}

因为渲染后:before和:after伪元素位于img元素里,故将img元素设为relative,:before作为背景,绝对定位覆盖默认裂图提示,:after作为提示文字居中在img元素中。

这种方法实现很方便,而且样式可控,但是兼容性实在太差,只有Chrome支持,其他浏览器都无法在图片加载失败后渲染出:before和:after伪元素。

翻了翻规范 https://www.w3.org/TR/CSS22/g...

This specification does not fully define the interaction of :before and :after with replaced elements (such as IMG in HTML). This will be defined in more detail in a future specification.

所以,替换元素的:before、:after伪元素由浏览器产商自己实现,兼容性如下:

浏览器 Chrome 54 Safari 10.0 Firefox 50.0 IE 8
兼容性
方案二:利用img元素的error事件

与加载成功事件load相似,加载失败也会触发对应的事件——error。我们可以利用error事件,当加载失败时替换img的src为加载失败提示图的url。

关键代码如下:

Array.from(document.getElementsByTagName("img")).forEach(img => {
  img.onerror = () => {
      img.onerror = null;
      img.src = "data:image/gif;base64...";
  }
});

其中error响应事件中要记得将img的onerror属性置null,否则一旦加载失败提示图也加载失败时会陷入死循环。

该方案可以支持当前主流浏览器:

浏览器 Chrome 54 Safari 10.0 Firefox 50.0 IE 8
兼容性

以上两种替换图片加载失败裂图提示的方案,第一种因兼容性的关系只能用于特殊情况,第二种方案需要js配合,但无兼容性问题。

参考文献

http://perishablepress.com/3-...

http://www.zhangxinxu.com/wor...

http://www.cnblogs.com/a_bu/a...

文章版权归作者所有,未经允许请勿转载,若此文章存在违规行为,您可以联系管理员删除。

转载请注明本文地址:https://www.ucloud.cn/yun/81908.html

相关文章

  • 图片加载相关技术

    摘要:本小节将讨论与图片加载时机相关的技术预加载与懒加载。同样,可将请求成功与失败的回调,作为图片预加载成功与失败的处理。 目前,出于性能与灵活性方面的考虑,我们都将一些小图片替换成矢量图或者字体。除了这些能被替换的小图之外,还有一些不得不使用位图的场景,如照片、背景等。对于这些位图,我们需要考虑它们在加载过程中的不同状态,而制定不同的表现方案。 图片加载过程中的状态,大致包括: 何时决定...

    junnplus 评论0 收藏0
  • 图片加载相关技术

    摘要:本小节将讨论与图片加载时机相关的技术预加载与懒加载。同样,可将请求成功与失败的回调,作为图片预加载成功与失败的处理。 目前,出于性能与灵活性方面的考虑,我们都将一些小图片替换成矢量图或者字体。除了这些能被替换的小图之外,还有一些不得不使用位图的场景,如照片、背景等。对于这些位图,我们需要考虑它们在加载过程中的不同状态,而制定不同的表现方案。 图片加载过程中的状态,大致包括: 何时决定...

    Leo_chen 评论0 收藏0
  • Bitmap避免OOM

    摘要:我知道最近世麟心情是错综复杂,但还好的是绝大多数程序员都是特别有善心的。如果图像数据较大就会造成对象申请的内存较多,如果图像过多就会造成内存不够用自然就会出现的现象。第二次将设置为再次调用函数时就能生成了。 目录介绍 01.先看一个需求分析案例 02.Bitmap占用内存介绍 03.影响Bitmap占用内存因素 04.图像加载的方式 05.加载图像内存去哪里了 06.具体实现加载图片...

    EastWoodYang 评论0 收藏0
  • Android 图片相关整理

    摘要:高斯模糊实现的代码设置高斯模糊代码设置模糊背景获取轮播图索引处的图片压缩图片压缩图片,宽高缩放图片高斯模糊图片高斯模糊建议模糊度在到之间得到模糊后的上下文半径将缩小后的图片做为预渲染的图片。 目录介绍 0.思考问题及解决方案 1.加载图片的压缩处理技术 2.网络url图片转换Bitmap保存到本地 2.1 直接通过http请求网络图片通过流转化成Bitmap 2.2 使用第三方库...

    darkerXi 评论0 收藏0
  • Android开源库

    摘要:的出现可以追溯到去年下拉刷新库,这一个就够了新鲜出炉的开源库。知乎开源,简洁,易用,不错的图片选择库。年初你绝对想尝试的个新安卓库这是一份年,月份发布的个最佳安卓库的列表,你应该会喜欢,虽然是按顺序排列的,但排名不分先后。 UI 之自定义 Behavior 实现 AppBarLayout 越界弹性效果 使用自定义 Behavior 实现 AppBarLayout 的弹性越界效果 仅需一...

    李涛 评论0 收藏0

发表评论

0条评论

阅读需要支付1元查看
<