一、说明
我们对图像性能优化和使图像在网络上快速加载充满热情。最有趣的探索领域之一是占位符:当图像尚未加载时要显示的内容。
在过去,我遇到了一些使用 SVG 的加载技术,我想在这篇文章中描述它们。
在这篇文章中,我们将介绍以下主题:
- 不同类型的占位符概述
- 基于 SVG 的占位符(边缘、形状和轮廓)
- 自动化流程。
二、不同类型的占位符概述
过去我写过关于占位符和图像延迟加载的文章,也谈到过它。在对图像进行延迟加载时,最好考虑将哪些内容呈现为占位符,因为它会对用户的感知性能产生重大影响。过去,我描述了几个选项:
在加载图像之前填充图像区域的几种策略。
- 为图像保留空白空间:在响应式设计的世界里,这可以防止内容跳来跳去。从用户体验的角度来看,这些布局更改是不好的,而且对性能也是如此。浏览器每次获取图像尺寸时都必须进行布局重新计算,从而为其留出空间。
- 占位符:假设我们正在显示用户的个人资料图像。我们可能希望在背景中显示轮廓。这在加载主图片时显示,但在请求失败或用户根本没有设置任何个人资料图片时也会显示。这些图像通常是基于矢量的,并且由于它们的尺寸小,是内联的良好候选者。
- 纯色:从图像中获取一种颜色,并将其用作占位符的背景色。这可以是主色,最有活力...这个想法是,它基于您正在加载的图像,应该有助于使无图像到加载图像之间的过渡更加平滑。
- 模糊图像:也称为模糊技术。渲染图像的微小版本,然后过渡到完整版本。初始图像在像素和 kB 中都很小。为了消除伪影,图像被放大和模糊。我之前写过关于 媒介如何进行渐进式图像加载, 使用 WebP 创建微小的预览图像, 以及 更多渐进式图像加载示例 .
事实证明,还有许多其他变体,许多聪明人正在开发其他技术来创建占位符。
其中之一是使用渐变而不是纯色。渐变可以创建更准确的最终图像预览,开销很小(有效载荷增加)。
使用渐变作为背景。来自Gradify的屏幕截图,它不再在线。GitHub 上的代码。
另一种技术是使用基于图像的SVG,这在最近的实验和黑客攻击中得到了一些牵引力。
二、基于 SVG 的占位符
我们知道 SVG 是矢量图像的理想选择。在大多数情况下,我们希望加载位图,因此问题是如何矢量化图像。一些选项使用边缘、形状和区域。
2.1 边缘
在上一篇文章中,我解释了如何找出图像的边缘并创建动画。我最初的目标是尝试绘制区域,矢量化图像,但我不知道该怎么做。我意识到使用边缘也可以是创新的,我决定将它们动画化,以创建“绘图”效果。
使用边缘检测和 SVG 动画绘制图像
过去,SVG 几乎没有被使用和支持。在我们开始使用它们作为经典的替代品一段时间后......
medium.com
2.2 形状
SVG 还可用于从图像而不是边缘/边框绘制区域。在某种程度上,我们将矢量化位图图像以创建一个占位符。
过去,我试图用三角形做类似的事情。你可以在我在CSSConf和Render Conf的演讲中看到结果。
上面的代码笔是由 245 个三角形组成的基于 SVG 的占位符的概念证明。三角形的生成基于使用 Possan's polyserver 的 Delaunay 三角测量。正如预期的那样,SVG 使用的三角形越多,文件大小就越大。
2.3 Primitive 和 SQIP,一种基于 SVG 的 LQIP 技术
Tobias Baldauf一直在研究另一种使用SVG的低质量图像占位符技术,称为SQIP。在深入研究 SQIP 本身之前,我将概述 Primitive,这是一个 SQIP 所基于的库。
原始人非常迷人,我绝对建议您检查一下。它将位图图像转换为由重叠形状组成的 SVG。它的小尺寸使其适合将其直接内联到页面中。在初始 HTML 有效负载中减少一个往返,并减少一个有意义的占位符。
Primitive 基于三角形、矩形和圆形(以及其他一些)等形状生成图像。在每一步中,它都会添加一个新步骤。步骤越多,生成的图像看起来更接近原始图像。如果您的输出是 SVG,这也意味着输出代码的大小会更大。
为了理解Primitive的工作原理,我通过几个图像运行了它。我使用 10 个形状和 100 个形状为图稿生成了 SVG:
使用基元处理此图片,使用 10 个形状和 100 个形状。
当使用10个形状的图像时,我们开始掌握原始图像。在图像占位符的上下文中,有可能使用此 SVG 作为占位符。实际上,具有 10 个形状的 SVG 的代码非常小,大约 1030 字节,当通过 SVGO 传递输出时,它会下降到 ~640 字节。
<svg xmlns=”http://www.w3.org/2000/svg" width=”1024" height=”1024"><path fill=”#817c70" d=”M0 0h1024v1024H0z”/><g fill-opacity=”.502"><path fill=”#03020f” d=”M178 994l580 92L402–62"/><path fill=”#f2e2ba” d=”M638 894L614 6l472 440"/><path fill=”#fff8be” d=”M-62 854h300L138–62"/><path fill=”#76c2d9" d=”M410–62L154 530–62 38"/><path fill=”#62b4cf” d=”M1086–2L498–30l484 508"/><path fill=”#010412" d=”M430–2l196 52–76 356"/><path fill=”#eb7d3f” d=”M598 594l488–32–308 520"/><path fill=”#080a18" d=”M198 418l32 304 116–448"/><path fill=”#3f201d” d=”M1086 1062l-344–52 248–148"/><path fill=”#ebd29f” d=”M630 658l-60–372 516 320"/></g></svg>
正如预期的那样,用 100 个形状生成的图像更大,在 SVGO 之后权重为 ~5kB(之前为 8kB)。它们具有很高的细节水平,有效载荷仍然很小。决定使用多少三角形将在很大程度上取决于图像的类型(例如对比度,颜色数量,复杂性)和细节水平。
可以创建一个类似于 cpeg-dssim 的脚本来调整使用的形状数量,直到达到结构相似性阈值(或在最坏情况下达到最大形状数)。
这些生成的 SVG 也非常适合用作背景图像。由于尺寸受限且基于矢量,它们是英雄图像和大背景的良好候选者,否则会出现伪影。
2.5 平方
用托比亚斯自己的话说:
SQIP 试图在这两个极端之间找到平衡:它利用 Primitive 生成由几个简单形状组成的 SVG,这些形状近似于图像中可见的主要特征,使用 SVGO 优化 SVG 并向其添加高斯模糊滤镜。这将生成一个 SVG 占位符,其重量仅为 ~800–1000 字节,在所有屏幕上看起来都很流畅,并提供即将出现的图像内容的视觉提示。
结果类似于使用微小的占位符图像进行模糊技术(Medium和其他网站所做的)。不同之处在于,占位符不是使用位图图像,例如 JPG 或 WebP,而是 SVG。
如果我们对原始图像运行 SQIP,我们将得到:
对第一张图片和第二张图片使用 SQIP 输出图像。
输出 SVG 为 ~900 字节,检查代码我们可以发现应用于形状组的过滤器:feGaussianBlur
<span style="color:rgba(0, 0, 0, 0.8)"><span style="background-color:#ffffff"><span style="background-color:#f2f2f2"><span style="color:#242424"><svg xmlns="<a data-cke-saved-href="http://www.w3.org/2000/svg" href="http://www.w3.org/2000/svg" class="af ll">http://www.w3.org/2000/svg</a>" viewBox="0 0 2000 2000"><strong><filter id="b"><feGaussianBlur stdDeviation="12" /></filter></strong><path fill="#817c70" d="M0 0h2000v2000H0z"/><g filter="<strong>url(#b)</strong>" transform="translate(4 4) scale(7.8125)" fill-opacity=".5"><ellipse fill="#000210" rx="1" ry="1" transform="matrix(50.41098 -3.7951 11.14787 148.07886 107 194.6)"/><ellipse fill="#eee3bb" rx="1" ry="1" transform="matrix(-56.38179 17.684 -24.48514 -78.06584 205 110.1)"/><ellipse fill="#fff4bd" rx="1" ry="1" transform="matrix(35.40604 -5.49219 14.85017 95.73337 16.4 123.6)"/><ellipse fill="#79c7db" cx="21" cy="39" rx="65" ry="65"/><ellipse fill="#0c1320" cx="117" cy="38" rx="34" ry="47"/><ellipse fill="#5cb0cd" rx="1" ry="1" transform="matrix(-39.46201 77.24476 -54.56092 -27.87353 219.2 7.9)"/><path fill="#e57339" d="M271 159l-123–16 43 128z"/><ellipse fill="#47332f" cx="214" cy="237" rx="242" ry="19"/></g></svg></span></span></span></span>
SQIP 还可以输出一个图像标签,其中 SVG 内容 Base 64 编码:
<span style="color:rgba(0, 0, 0, 0.8)"><span style="background-color:#ffffff"><span style="background-color:#f2f2f2"><span style="color:#242424"><img width="640" height="640" src="example.jpg” alt="Add descriptive alt text" style="background-size: cover; background-image: url(data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAw…<stripped base 64>…PjwvZz48L3N2Zz4=);"></span></span></span></span>
2.6 轮廓
我们刚刚看了一下将 SVG 用于边缘和基本形状。另一种可能性是矢量化图像“追踪”它们。Mikael Ainalem 几天前分享了一个代码笔,展示了如何使用 2 色轮廓作为占位符。结果真的很漂亮:
在这种情况下,SVG是手绘的,但该技术很快催生了与工具的集成,以自动化该过程。
- Gatsby,一个使用 React 的静态站点生成器现在支持这些跟踪的 SVG。它使用 POTRACE 的 JS PORT 来矢量化图像。
- Craft 3 CMS,还增加了对轮廓的支持。它使用potrace的PHP端口。
- image-trace-loader,一个使用potrace来处理图像的Webpack加载器。
看到Emil的webpack加载器(基于potrace)和Mikael的手绘SVG之间的输出比较也很有趣。
我假设 potrace 生成的输出使用的是默认选项。但是,可以调整它们。检查图像跟踪加载器的选项,这些选项几乎是传递给potrace的选项。
三、总结
我们已经看到了从图像生成 SVG 并将其用作占位符的不同工具和技术。就像 WebP 是一种很棒的缩略图格式一样,SVG 也是一种在占位符中使用的有趣格式。我们可以控制细节级别(以及大小),它是高度可压缩的,并且易于使用CSS和JS进行操作。