前言
在前面一文使用交叉点观察器延迟加载图像以提高性能中,已经知晓了使用该方式可以提高页面的访问速度,那在此基础上,我们还可以做得更好?,答案显而易见,如果你爬梯子访问过一些国外的图片类的网站,国内若常混迹于知乎,简书,掘金等社区,你也总会看到比较炫酷的体验效果,就是很多图片从非常模糊状态过渡到清晰的图像,这是怎么实现的?本文将为你揭晓,在自己的实际开发中,可以尝试将此skill运用到项目中,如果文中有误导的地方,欢迎路过的老师多提意见和指正
目录:
- 开始入门(下载安装go,命令行终端下安装SQIP工具)
- 使用交叉点观察者进行延迟加载(核心js实现)
- 总结(使用低质量图像占位符(SQIP)与使用Intersection Observer的延迟加载技术结合使用时,节省带宽,提升性能)
正文从这里开始~
如果你定期访问诸如Facebook,Pinterest或Medium等网站,你可能已经注意到,第一次加载页面时,你将会看到低质量或模糊图像的页面。然后,随着页面继续加载,模糊/低质量图像将被替换为全质量版本。要看到这个实例的例子,让我们来看看下面的图片
上面的图片是中间载入页面的屏幕截图。左侧的屏幕截图显示了首次加载低质量图像时的页面,然后右侧的屏幕截图显示了页面完成加载后的页面,并显示了完整的质量图像
这种图像加载技术被称为LQIP(低质量图像占位符),几年前由Guy Podjarny首次提出。这种技术背后的想法是,在连接速度较慢的情况下,你可以尽快向用户展示完全可用的网页,为他们提供更好的体验。即使在更好的网络连接上,这仍然为用户提供了更快的可用页面,并且体验得到了改善。从网络性能的角度来看,这意味着你的网页的可用版本将加载得更快,并且(取决于其他因素),你应该有更快的时间来开始有意义的绘制
事实上,在今年的Performance Calendar中,Tobias Baldauf撰写了一篇关于LQIP加载技术的深度文章,他创建了一个名为SQIP的工具
SQIP是一种创建低质量图像版本的工具,作为SVG可用作占位符,然后在连接允许时加载完整质量版本。我最近开始尝试使用SQIP,开始创建低质量版本的图像可能非常有趣
前段时间,我使用Intersection Observer写了一个图像延迟加载技术。延迟加载图像背后的想法是,你需要等到用户进一步向下滚动页面,并在发出网络请求之前将图像放入视图中。如果你的网页包含多个图像,但你只能在滚动查看图像时加载每个图像,则最终会节省带宽,并确保网页加载速度更快
这让我思考;我想知道是否可以将交叉观察者和使用Tobias的SQIP工具创建的低质量占位符图像结合起来。一旦我开始进一步尝试,它比我想象的更容易。使用延迟加载技术将意味着用户只加载他们在视口中看到的内容,而与低质量图像相结合则意味着双重网页性能会带来麻烦
在这篇文章中,我将通过我所经历的步骤和您如何开始使用这种技术来谈谈您自己
开始入门
在我们继续之前,我们需要安装SQIP。在我们开箱即用这个工具之前,需要先安装一些先决条件。首先,你需要安装Go(百度GO官网下载或者去中文网址下载相应go并安装,检测go是否安装,命令行下输入go)。起初我发现这个装置有点棘手,但遇到了这篇精彩的文章,指出了我的正确方向。一旦安装Go,您需要安装一个名为Primitive的工具。使用以下命令可以从终端轻松完成此操作
代码语言:javascript复制go get -u github.com/fogleman/primitive(可以使用npm 安装npm install -g primitive)
最后,我们可以使用以下命令安装SQIP
代码语言:javascript复制npm install -g sqip
我们现在准备开始使用SQIP创建低质量的占位符图像。我选择了一张随机的狗作为我的测试图像(谁不喜欢狗!)。为了处理我们的图像,我们需要在终端中运行以下命令
代码语言:javascript复制sqip -o dog.svg dog.jpg
上述命令将启动SQIP工具,处理dog.jpg图像并吐出一个名为dog.svg的低质量占位符文件。现在新处理的图像看上去有点像以下内容
命令行下(git/cmd)下使用sqip工具将实际的图片进行模糊化处理
用SQIP处理完后,该图片会指定在img标签的src中
未通过SQIP前,该实际图片会指定在img标签的data-src中
左边的图片显示了低质量的SVG版本,右边的图片是完整的质量版本。关于这个工具的好处是这个图像的低质量版本只有800字节 - 令人惊叹,在本地服务器中可进行测试,我示例中的图片svg占900字节,具体以你自己测试的为准
使用交叉点观察者进行延迟加载
现在我们有了两个版本的图像,我们可以开始懒加载它们。如果你以前从未听说过交叉观测器,它将内置到大多数现代浏览器中,并让你知道观察到的元素何时进入或退出浏览器的视口。这使得它非常理想,因为它能够异步传递数据,不会影响主线程,使其成为向您提供反馈的有效手段
如果你曾经使用过传统的图片延迟加载器,那么你将会意识到,几乎所有这些库都会使用滚动事件或使用定期计时器来检查元素的边界,然后再确定它是否在视图中。这种方法的问题在于,它迫使浏览器重新布局整个页面,并且在某些情况下会引起相当大的麻烦到你的网站。我们可以使用相交观测器做得更好
在本文中,我将着重介绍这种延迟加载技术的基础知识
好吧,让我们开始吧。设想一个基本的HTML页面,其中包含三个与上图类似的图像。在网页上,你将拥有与以下代码类似的图片元素
代码语言:javascript复制<img class="js-lazy-image" src="dog.svg" data-src="dog.jpg">
在上面的代码中,你可能会注意到图像标签中有两个图像源。首先,我们在页面加载时加载dog.svg图像,这是我们的低质量图像。接下来,我们使用一个名为data-src的数据属性指向全质量图像源。我们将使用它来尽快替换低质量图像和全面质量的图像。你可能还会注意到,image元素也有一个名为js-lazy-image的类 - 它用于JavaScript代码中以确定我们想要延迟加载哪些元素
我创建了一个名为lazyload.js的JavaScript文件 - 它包含以下代码
代码语言:javascript复制// 获取所有标记为延迟加载的图像 Get all of the images that are marked up to lazy load像
const images = document.querySelectorAll('.js-lazy-image');
const config = {
// 如果图像在Y轴上达到50像素以内,请开始下载 If the image gets within 50px in the Y axis, start the download.
rootMargin: '50px 0px',
threshold: 0.01
};
// 页面上图像的观察者 The observer for the images on the page
let observer = new IntersectionObserver(onIntersection, config);
images.forEach(image => {
observer.observe(image);
});
}
上面的例子看起来像很多代码,但让我们一步一步分解它。首先,我选择页面上具有js-lazy-image类的所有图像。接下来,我创建一个新的IntersectionObserver,并使用它观察我们选择的具有类js-lazy-image的所有图像。
使用IntersectionObserver的默认选项,当元素部分进入视图并完全离开视口时,你的回调将被调用。在这种情况下,我正在通过一些额外的配置选项到IntersectionObserver。使用rootMargin允许你为根指定页边距,从而有效地允许你增大或缩小用于交点的区域。我们希望确保如果图像在Y轴上达到50像素以内,我们将开始下载
现在我们已经创建了一个交叉点观察器,并且正在观察页面上的图像,我们可以利用交叉点事件,当元素进入视图时将会触发
代码语言:javascript复制function onIntersection(entries) {
// 循环输入条目 Loop through the entries
entries.forEach(entry => {
// 我们在视口中 Are we in viewport?
if (entry.intersectionRatio > 0) {
// 停止观看并加载图像 Stop watching and load the image
observer.unobserve(entry.target);
preloadImage(entry.target);
}
});
}
在上面的代码中,只要我们正在观察的元素进入用户的视口,就会触发onIntersection函数。此时,我们可以遍历我们正在观察的图像,并确定哪个图像处于视口中。如果当前元素处于相交比中,我们知道该图像位于用户视口中,我们可以加载它。加载图像后,我们不需要再观察它,并且使用unobserve()将从交叉观察者的条目列表中将其删除。而已!只要用户滚动并且图像进入视图,相应的图像就会被加载
如果你想要测试这些代码,我已经创建了一个演示页面,可以在deanhume.github.io/lazy-observer-load找到它。为了让你更全面地了解整个网页的外观,让我们来想象下面的页面
你会注意到,因为中间图像位于用户的视口中,所以它被延迟加载,并且低质量图像被替换为全质量图像。视口下方的所有东西(红线)仍然模糊不清。如果用户滚动到这些图像,这些图像只会被替换,节省用户带宽并确保页面加载速度更快
如果你正在以快速连接测试此演示,您甚至可能不会注意到图像被换出。我发现最好的测试方法是在Chrome开发人员工具中启用网络限制并禁用缓存
这是示例中简易的HTML代码:
代码语言:javascript复制<img class="js-lazy-image centered js-lazy-image--handled fade-in" src="./img/dog-running.jpg" width="400" height="400" data-src="./img/dog-running.jpg">
<br /><br /><br /><br /><br />
<img class="js-lazy-image centered" src="./img/dog-face.svg" width="400" height="400" data-src="./img/dog-face.jpg">
<br /><br /><br />
<img class="js-lazy-image centered" src="./img/dog-battersea.svg" width="400" height="400" data-src="./img/dog-battersea.jpg">
css代码:主要是找到元素添加样式,居中,动画淡入效果
代码语言:javascript复制.centered {
display: block;
margin: 0 auto;
}
.fade-in {
animation-name: fadeIn;
animation-duration: 1.3s;
animation-timing-function: cubic-bezier(0, 0, 0.4, 1);// 规定动画的速度曲线,0到1之间的值,4个点描述整个曲线的运动形状
animation-fill-mode: forwards; // 该属性规定动画在播放之前或之后,其动画效果是否可见,此处规定当动画完成后,保持最后一个属性值,设置为动画的最后一帧的样式
}
img[Attributes Style] {
width: 400px;
height: 400px;
}
@keyframes fadeIn {
from {
opacity: 0;
}
to {
opacity: 1;
}
}
完整的js示例代码:
代码语言:javascript复制/**
*
* @authors 随笔川迹 (itclanCode@163.com)
* @date 2018-03-27 1:43:56
* @version $Id$
* @weChatPublicId ((itclanCoder))
* @QQGroup ((643468880))
* @PersonWeChatId ((suibichuanji))
* @PersonQQ ((1046678249))
* @link ((https://juejin.im/post/5a005392518825295f5d53c8))
* @describe 使用相交观察器进行渐进式图像加载js代码*/
'use strict';
// 获取图片元素
var images = document.querySelectorAll('.js-lazy-image'),
config = {
rootMargin: '50px 0px',
threshold: 0.01
},
imageCount = images.length,
observer;
if (!('IntersectionObserver'in window))
loadImagesImmediately(images);
else {
observer = new IntersectionObserver(onIntersection,config);
for (var image, i = 0; i < images.length; i )
(image = images[i],
!image.classList.contains('js-lazy-image--handled')) && observer.observe(image)
}
function fetchImage(a) {
return new Promise(function(b, c) {
var d = new Image;
d.src = a,
d.onload = b,
d.onerror = c
}
)
}
function preloadImage(a) {
var b = a.dataset.src;
return b ? fetchImage(b).then(function() {
applyImage(a, b)
}) : void 0
}
function loadImagesImmediately(a) {
for (var d, b = Array.from(a), c = 0; c < a.length; c )
d = a[c],
preloadImage(d)
}
function disconnect() {
observer && observer.disconnect()
}
function onIntersection(a) {
0 === imageCount && observer.disconnect();
for (var c, b = 0; b < a.length; b )
c = a[b],
0 < c.intersectionRatio && (imageCount--,
observer.unobserve(c.target),
preloadImage(c.target))
}
function applyImage(a, b) {
a.classList.add('js-lazy-image--handled'),
a.src = b,
a.classList.add('fade-in')
}
这是我在本地服务器上分别对jpg与svg测试的结果如上图所示
测试示例链接地止:https://deanhume.github.io/lazy-observer-load/
总结
使用低质量图像占位符(LQIP)确保您的用户可以更快地获得可用页面。当与使用Intersection Observer的延迟加载技术结合使用时,用户也可以节省带宽。尝试SQIP很有趣,其实这种做法就是在首屏加载图像时,以低质量模糊图像加载过渡到清晰图像,在体积上,经过SQIP处理后,与实际图片比较起来,可以看出容量还更小,更多的做法,从各个网站上看出,他们的处理方式都很类似,页面上同一张图片用两种存储格式
当触发某个条件,加载到该图片时,先加载低质量体积小的图片,然后快速的被该实际图片尺寸给替换。至于优化图片,可以将图片压缩,cdn加速,雪碧图等的.而svg是一种矢量图形,基于像素存储数据,而是通过记录坐标的形式存储图形信息。SVG使用基于XML的语义化标签结构,这有点像HTML。由于是DOM结构,你可以通过ID获取SVG元素,并操纵它们。这带来了很多可能性,例如使用JavaScript和CSS 修改并对元素进行动画操作或者创建响应式图形,比如阿里的svg图标等的
至于面试的时候,当回答图片优化时,图片选择(jpg/jpeg,gif,png,TIFF(mac)等),懒加载,压缩,以及webgl,canvas,以及文中SQIP创建低质量图像版本的工具生成svg格式,个人觉得,回答后者令人要夸目得多
原文出处:https://calendar.perfplanet.com/2017/progressive-image-loading-using-intersection-observer-and-sqip/?utm_source=mobiledevweekly&utm_medium=email
作者:川川,一个靠前排的90后帅小伙,争做一个具有情怀的代码男,愿做你耳朵旁边的暖男,眼睛笔尖下的朋友,我有故事,你有酒,我们约?