SVG占位图技术

介绍基于SVG的图像加载技术



从图像中生成的SVG可以作为占位图哦~

本文讨论如下内容:

  • 占位图技术总览
  • 基于SVG的占位图
  • 自动化实现

现有占位图技术

在进行图像的懒加载时考虑使用怎样的占位图来渲染是一个很好的想法,这对用户的感知体验有较大的影响。以前常用的占位图方法如下:



  1. 给图像预留空间
    在响应式设计的世界中,这种方法避免了凭空出现的内容区域,不过从用户体验的角度来看这种布局的变化是不好的,对于性能来说也是如此:浏览器每次获取图像的尺寸后都要被迫重新计算布局。
  2. 占位图
    假如要显示一个用户头像,会先显示一个人物轮廓作为背景,当加载时显示这个图案,这种通常都是矢量图,尺寸较小且易于嵌入。
  3. 纯色
    选择图像的主色调作为占位图的背景颜色,这个颜色是基于所加载图像的,并且从纯色到加载后的图像有一个平滑的过渡。
  4. 模糊图
    先使用一个小尺寸的模糊图像渲染,然后加载完整图像,初始的图像不论在像素(px)还是尺寸(kB)上都是最小的。

使用SVG作为占位图

SVG是矢量图的一种理想选择,大多数情况下我们需要加载的是位图,那么问题就变成了如何矢量化一个图像,常用的方案是矢量化边、形状和区域。

边 - Edges

使用这篇文章中的方法检测图像中的边并创建路径动画。如下面这个例子,检测出边后创建了一个“绘制”的动画效果。

See the Pen Contour by José Manuel Pérez (@jmperez) on CodePen.

形状 - Shapes

同样可以使用SVG的形状进行绘制,这样一定程度上可以矢量化位图来创建占位图。

下面这个例子是使用三角形的一个尝试,可以看看作者在CSSConfRender Conf的分享。

See the Pen Image loading with Delaunay triangulation based placeholder by José Manuel Pérez (@jmperez) on CodePen.

上面这个codepe是使用245个三角形融合的一个SVG占位图。三角形是使用基于Delaunay三角分解polyserver生成的。预料之中的是,使用的SVG三角形越多文件尺寸越大。

Primitive与SQIP

Tobias Baldauf在低质量占位图(Low-Quality Image Placeholder)技术的基础上结合SVG做出了SQIP项目,在了解SQIP前可以先看看Primitive,SQIP就是基于它的项目。

Primitive是个很棒的项目建议你了解下,它将位图转换成了一个由多个重叠形状融合的SVG。小尺寸的特点使其适应嵌入到页面中,并且减少一次(请求)往返对于初始的HTML有效载荷是很有意义的。

Primitive使用三角形、矩形与圆形等形状来生成图像,执行过程的每一步都会添加一个图形,结果图看起来很接近原始图,不过若输出为SVG的话意味着输出的代码量会变大。

为了便于理解Primitive的工作方式,展示一下执行结果,使用10与100个图形来生成SVG:

可以看出,当使用10个形状来构成图像时就可以看出原始图像的模样来。在占位图的上下文中可以使用SVG作为占位图。实际上10个形状的SVG代码量很小,也就1030字节,在使用SVGO输出后可以压缩到~640字节。

1
<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),在依然较小的HTML payload下保持了较高等级的细节,使用三角形数量的多少基于取决于图像的类型(对比度、颜色数量、复杂度)与细节的等级。

也可以创建一个类似cpeg-dssim的脚本来调整形状的数量使其达到一个结构相似度阈值(或者最坏情况下的最大形状数)。

输出的SVG也可以作为背景图。由于可限制尺寸与矢量化的特点也很适合作为首图(hero image)与大背景图。

SQIP

Tobias:

SQIP is an attempt to find a balance between these two extremes: it makes use of Primitive to generate a SVG consisting of several simple shapes that approximate the main features visible inside the image, optimizes the SVG using SVGO and adds a Gaussian Blur filter to it. This produces a SVG placeholder which weighs in at only ~800–1000 bytes, looks smooth on all screens and provides an visual cue of image contents to come.

使用这个工具生成的结果图有点像在一个小尺寸占位图上使用模糊技术后的效果,不同点在于前者不是位图图像(比如JPG或WebP),而是SVG。

若在一个原始图像上执行SQIP会得到如下结果:

输出的SVG大小为~900字节,看一下代码会发现在形状上应用了feGaussianBlur滤镜:

1
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 2000 2000"><filter id="b"><feGaussianBlur stdDeviation="12" /></filter><path fill="#817c70" d="M0 0h2000v2000H0z"/><g filter="url(#b)" 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>

SQIP也可以输出将SVG内容进行Base64编码后的image标签:

1
<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=);">

轮廓 - Silhouettes

我们已经了解了使用SVG线条和形状的方法,还有一种可能是使用“描摹”的方法来矢量化图像。Mikael Ainalem不久前分享了一个codepen,展示了将二色轮廓作为占位图的方法,效果看起来很棒:

这里的SVG虽然是手绘的,不过这种技术很快就被集成在工具中实现了自动化:

potrace是一个矢量化位图的工具

假定这些使用potrace生成的图像都使用的默认设置,然而这是可以调整的。看看image-trace-loader中的设置选项,跟potrace中的设置基本相同。

总结

我们看到了许多从图像生成SVG并作为占位图的工具与技术。就跟WebP是一种很棒的用于略缩图的格式,SVG也是一种有趣的用于占位图的格式。使用这种方法可以调整细节程度(也就跟尺寸有关了),有较高的压缩率,并且可以方便地使用CSS和JS进行操作。

参考

文章目录
  1. 1. 现有占位图技术
  2. 2. 使用SVG作为占位图
    1. 2.1. 边 - Edges
    2. 2.2. 形状 - Shapes
    3. 2.3. Primitive与SQIP
    4. 2.4. SQIP
    5. 2.5. 轮廓 - Silhouettes
  3. 3. 总结
  4. 4. 参考
|