深入浅出svg(其一)认识svg

528 阅读14分钟

参考文献:

  1. MDN文档
  2. ndz, 案例+图解带你一文读懂SVG
  3. 三阶贝塞尔曲线拟合圆弧的一般公式

三阶贝塞尔曲线拟合一般圆的方案在我的个人组件中已经实现: getClipPathValue.ts

什么是SVG

基本概念

SVG(Scalable Vector Graphics)是一种基于XML的矢量图形格式,用于在网络上描述二维图形。SVG由W3C开发,支持三种图形类型:矢量图形、栅格图像.bit(位图)和文本text。

特性

  • svg是矢量图,这意味着svg图像在缩放时可以保证边缘平滑和清晰。
  • svg是基于XML的图形格式,这意味着我们可以像编辑html一样编辑svg代码。并且可以直接嵌入html中,可以直接被CSS和JS所控制

缺点

  • SVG复杂度越高渲染速度就会越慢(任何过度使用DOM的应用都不快)
  • SVG不适合游戏应用,只能结合Canvas来实现
  • SVG不能动态的修改动画内容

常见属性

下面以一个svg图像为例,讲解下svg的基本用法

<svg width="200" height="200" viewBox="0 0 200 200" stroke-width="5" fill="none" xmlns="http://www.w3.org/2000/svg" >
    <rect width="200" height="200" fill="#C55D5D"/>
    <path d="M40 40H160" stroke="white"/>
    <rect x="40" y="70" width="50" height="50" stroke="white"/>
    <circle cx="145" cy="95" r="25" stroke="white"/>
    <polygon points="130 130 160 180 100 180" stroke="white"/>
</svg>        

width、height、stock-width

这些统一都是svg中和长宽高相关的属性值。

x、y、cx、cy

这些统一都是和svg中的坐标相关的属性值。

svg中的坐标系

svg中的坐标系和html坐标系一样,均是以左上角为原点,向右向下为x轴、y轴的正方向的坐标系

xmlns

xmlns属性表示svg的命名空间,他定义了svg文件中使用的元素和属性属于哪个xml命名空间,以便于浏览器正确的解析渲染svg的内容

svg的常见的命名空间有两种

  1. http://www.w3.org/2000/svg 这是SVG1.1的命名空间,svg文件最常见的命名空间
  2. http://www.w3.org/1999/xlink 这是xlink明明空间,用于在svg中实现超链接, 比较常见的一种用法是在一个svg中使用use标签引用另一个svg标签。这个在阿里巴巴的iconfont库下的symbol用法中会见到。

viewBox

基本组成:viewBox: "x y width height"

viewBox可以理解为一个镜头,x、y是视窗左上角点在画布上的位置,width和height是镜头的宽和高,viewBox的作用是 从 (x,y)开始,截取width * height高的区域,将它放到svg的画布上去展示。viewBox的操作其实是反直觉的。

举几个例子

  1. 将viewBox的宽高改为100,相当于截取svg上宽高为100的区域,将其放到200 * 200的svg画布上展示。所以显示的效果就是图像内容放大了

  1. 将viewBox的宽高改为400,相当于在svg图像上截取400 * 400的区域,将其放到200 * 200的svg画布上展示。所以显示的效果就是图像内容缩小了

  1. 如果想将图像居中,则只需要将摄像头的视角向左上角分别移动一定距离。

fill

fill属性用于填充图形的颜色

  • fill-opacity: 用于控制填充颜色的透明度

  • fill-rule

    • nonzero: nonzero为默认值,规则为:要判断一个点是否在图形内,从该点作任意方向的一条射线,然后检测射线与图形路径的交点情况。从0开始计数,这条射线与顺时针的路径相交,则计数+1,与逆时针的射线相交,则计数-1。最终结果为0,则判断该店在外部,否则判断为在内部
    • evenodd: 规则为:要判断一个点是否在图形内,从该点作任意方向的一条射线,然后检测射线与图形路径的交点的数量。如果结果是奇数则认为点在内部,是偶数则认为点在外部
  • Stroke

  • stroke属性用来定义线条、文本或元素轮廓的颜色。

    • stroke、stroke-width、stroke-opacity 分别表示轮廓的颜色、宽度、透明度
    • stroke-linecap:用于控制轮廓起点和终点的形状。
    • 参数类型:butt 以直边结束线段,round以圆角结束线段,圆角半径等于 1/2 * stroke-widthstroke-square 以正方形结束线段,会在末端增加一个边长为stroke-width的正方形
    • stroke-linejoin 用于控制轮廓链接处的轮廓样式
    • 参数类型:miter:尖角、round:圆角、bevel 斜面
    • 以上5个参数,举个例子
    • stroke-dasharray:将轮廓定义为虚线。
    • 示例:stroke-dasharray="100 40 10 100 50 100"
    • stroke-dashoffset:它定义了虚线与路径起点之间的偏移量。

SVG基本图形标签

circle

circle 标签能在屏幕上绘制一个圆形

语法:<circle cx="100" cy="100" r="100"/>

属性:cx、cy为圆的坐标,r为圆的半径

示例:

<svg width="40" height="40" viewBox="0 0 40 40" stroke-width="2" fill="none" xmlns="http://www.w3.org/2000/svg" >
  <circle cx="20" cy="20" r="18" stroke="white"/>
</svg>        

rect

rect标签能在屏幕上绘制一个矩形

语法:<rect x="0" y="0" rx="5" ry="5" width="300" height="200" rx="16" ry="16"/>

属性:x、y为矩形的起始点坐标,rx、ry为圆角x、y轴方向的半径, width、height为矩形的宽高,rx、ry则分别代表了矩形的border-radius的两个数值。

示例:

<svg width="40" height="40" viewBox="0 0 40 40" stroke-width="2" fill="none" xmlns="http://www.w3.org/2000/svg" >
    <rect x="2" y="2" width="30" height="15" stroke="white" rx="12" ry="6"/>
</svg>

ellipse

ellipse标可以进行椭圆的绘制。

语法:<ellipse cx="100" cy="100" rx="100" ry="50"/>

属性:cx、cy为椭圆长轴和短轴焦点的坐标,rx为椭圆的x轴半径、ry为椭圆的y轴半径

示例:

<svg width="40" height="40" viewBox="0 0 40 40" stroke-width="2" fill="none" xmlns="http://www.w3.org/2000/svg">
    <ellipse cx="20" cy="20" rx="15" ry="5" stroke="#fff"/>
</svg>

多边形(polygon)

polygon标签和polyline标签类似,都是由很多个点链接在一起的。但不同的是polygon路径中的最后一个点和第一个点是默认闭合的。

语法:<polygon points="0 0, 20 40, 70 80, 100 90, 200 30, 250 50" />

属性:points为点集数列,其中每个点都必须包含2个数字,一个是x坐标,一个是y坐标。

示例:

<svg width="100" height="100"  viewBox="0 0 100 100" fill="none" xmlns="http://www.w3.org/2000/svg">
    <polygon points="5 5, 12 14, 27 28, 30 49, 50 13, 80 90, 90 12" stroke="#fff" stroke-width="2" />
</svg>

Path路径元素

path元素是svg中最常见、应用最广泛最灵活的一个元素,利用path我们可以构建圆、多边形、弧形以及任意曲线。

path的基本形式

<path d="M50 50 H 200 V 200 H 50 L 50 50"/>

path的专有属性为d,路径定义是一个路径命令组成的列表,其基本形式是命令符号X Y...命令符号后面接坐标点。

Path的命令

SVG 定义了六种路径命令类型,一共 20 条命令。

  • MoveTo 移动命令:M m
  • LineTo 绘制直线:L H V l h v
  • 三次贝塞尔曲线:C S c s
  • 二次贝塞尔曲线:Q T q t
  • 弧线:A a
  • 闭合指令:Z z

命令是大小写敏感的。当命令使用大写字母的时候,后面的坐标表示绝对坐标,使用小写字母时,表示基于上一个点的相对坐标。除了闭合指令

M(Move to)和Z(Close path)指令的使用

每一个命令都需要以M开头,用以规定绘制的起始点。M可以多次使用。表示从当前位置移动到新的位置而不作任何绘制,并从新的位置开始绘制新的图形。

<svg width="50" height="50"  fill="none" xmlns="http://www.w3.org/2000/svg">
    <path d="M10 20V10 M10 30v10 M10 25h30 Z" stroke-width="2" stroke="#000"/>
</svg>

如果想要一段路径成为闭合路径,则需要以M开头,以Z结尾

<svg width="50" height="50"  fill="none" xmlns="http://www.w3.org/2000/svg">
    <path d="M10 20V10H10L42 25L40 30Z M25 25H43V42Z" stroke-width="2" stroke="#fff"/>
</svg>
直线指令L(Line to) H(Horizontal Line to) V(Vertical Line to)

H表示绘制一条水平直线,V表示绘制一条水平直线,L表示从当前点绘制一条直线到目标点。

基本用法

<svg width="50" height="50"  fill="none" xmlns="http://www.w3.org/2000/svg">
    <path d="M5 5H40 M10 10V40 M15 15L40 40" stroke-width="2" stroke="#fff"/>
</svg>
弧线指令 A (Arc)

A命令用于画弧形,它可以截取圆或椭圆的弧形成的曲线

语法:A rx ry x-axis-rotation large-arc-flag sweep-flag x y 或者 a rx ry x-axis-rotation large-arc-flag sweep-flag x y

参数:

  • rx、ry分别为X轴的半径和Y轴的半径
  • x-axis-rotation为弧度在X轴的旋转角度
  • large-arc-flag决定弧线是大于还是小于180度,0表示小角度弧,1表示大角度弧
  • sweep-flag为弧的方向,0表示从起点到终点沿逆时针画弧,1表示从起点到终点沿顺时针画弧
  • x、y为弧形的终点

首先 弧形的x轴半径和y轴半径以及弧形的重点非常好理解。举个例子

<svg width="320" height="160" xmlns="http://www.w3.org/2000/svg" stroke-width="2" stroke="red">
    <rect width="320" height="160" fill="#fff" />
    <path d="M50 100 A 30 50 0 0 1 150 100" fill="none"/>
 </svg>

50,100位置开始,绘制一个x轴30长度,y轴50长度的弧形,到150 100位置结束。

理解第三个参数

第三个参数为,弧度在X轴的旋转角度。将上一个案例的第三个参数改为60,则效果为

如果不能理解这个效果,我们可以作一些辅助线

补全整个椭圆后,上半部分的曲线也没那么奇怪了,实际上就是椭圆的X方向轴的旋转角度。

理解第四个、第五个参数

用A指令绘制弧线,可以理解为用一条直线将一个椭圆分成两个部分,而简单来说A指令的第四、第五个参数就是用于决定绘制的是哪一个部分的弧线

以下面的图形为例

  • 第四个参数,large-arc-flag 决定的是 所渲染的弧线位于这条直线分开的两条弧线的较长的还是较短的
  • 第五个参数,sweep-flag 决定的是 这条弧线是顺时针绘制还是逆时针绘制。

large-arc-flag 和 sweep-flag 共同决定了这条弧线的具体形态

<svg width="325" height="325" xmlns="http://www.w3.org/2000/svg">
  <path d="M 80 80
           A 45 45, 0, 0, 0, 125 125
           L 125 80 Z" fill="green"/>
  <path d="M 230 80
           A 45 45, 0, 1, 0, 275 125
           L 275 80 Z" fill="red"/>
  <path d="M 80 230
           A 45 45, 0, 0, 1, 125 275
           L 125 230 Z" fill="purple"/>
  <path d="M 230 230
           A 45 45, 0, 1, 1, 275 275
           L 275 230 Z" fill="blue"/>
</svg>

贝塞尔曲线指令 C S c sQ T q t

什么是贝塞尔曲线?

直接说概念可能比较抽象,但是接触过css、或者设计类软件的肯定都用过贝塞尔曲线。

贝塞尔曲线在css中常见的形式就是 ease-in-out这动画效果。

在设计软件中(以Figma为例),贝塞尔曲线如下所示

图形触发,我们可以看到贝塞尔曲线有曲线的两个端点。每个端点上有一个控制点,移动控制点可以更改曲线的形状。

那么 贝塞尔曲线 到底是什么?

贝塞尔曲线是一种通过特定的控制点来定义曲线形状的数学曲线。这种曲线广泛应用于计算机图形学、动画、工程设计等领域,因为它们可以非常灵活地表示形状,同时保持数学上的简洁和易于计算的特性。

对于一个n阶的贝塞尔曲线,其公式为

B(t)=i=0nPi(ni)(1t)niti\begin{equation} B(t) = \sum_{i=0}^{n} P_i \binom{n}{i} (1-t)^{n-i} t^i \end{equation}

在svg中,贝塞尔曲线以二阶或者三阶的形态存在,以更常见的三阶贝塞尔曲线为例,其公式分别为

B(t)=P0(1t)3+P1(1t)2t+P2(1t)t2+P3t3\begin{equation} B(t)=P_0(1-t)^3+P_1(1-t)^2t+P2(1-t)t^2+P_3t^3 \end{equation}

其绘制的过程如下图所示,P点经过的路径即为贝塞尔曲线的样子。

贝塞尔曲线的正向绘制

现在我们知道了贝塞尔曲线的四个点的意义,那么通过四个点,就可以用path的C指令就可以绘制一条贝塞尔曲线。

<svg width="175" height="175" viewBox="0 0 175 175" fill="none" xmlns="http://www.w3.org/2000/svg">
        <path d="M20 20C120 20 150 50 150 150" stroke="black"/>
</svg>

起始点为20 20,起始点附带的控制点坐标为120 20 终点为150 150 终点附带的控制点为150 50

贝塞尔曲线的逆向拟合

一般来说,我们对贝塞尔曲线的应用都是正向的。但是某些极特殊的情况下,我们也需要知道一些逆向贝塞尔曲线的拟合方式。

比如,下面这样的一个环扇。我们期望可以自由控制环扇的内径和外径,并将其以svg的方式进行绘制。

这个时候我们就需要对贝塞尔曲线进行逆向拟合。

以圆弧为例,贝塞尔曲线逆向拟合的原理在于根据圆心位置、半径宽度和圆弧角度,推测出贝塞尔曲线的四个点。下面进行简单推导。

通过圆心O绘制出半径为1的圆弧A到D,作AB和CD分别为圆弧的期限,设其长度为h

将ABCD四个点带入贝塞尔曲线三阶公式如下,P(t)即为圆弧上的每一点。

P(t)=A(1t)3+B(1t)2t+C(1t)t2+Dt3\begin{equation} P(t)=A(1-t)^3+B(1-t)^2t+C(1-t)t^2+Dt^3 \end{equation}

由于贝塞尔曲线具有对称性,因此不放将t = 1/2带入公式(3),易得

P(12)=18A+38B+38C+18D\begin{equation} P(\frac{1}{2})=\frac{1}{8}A+\frac{3}{8}B+\frac{3}{8}C+\frac{1}{8}D \end{equation}

将ABCD四给点带入公式(4)。可以得到以下方程

[xy]=[cosθ18+(cosθ+hsinθ)38+138+118sinθ18+(sinθhcosθ)38+h38+018]=[cosθ2sinθ2]\begin{equation} \begin{bmatrix} x \\ y \end{bmatrix} = \begin{bmatrix} \cos \theta\cdot\frac{1}{8} + \left(\cos \theta + h \sin \theta\right) \frac{3}{8} + 1 \cdot \frac{3}{8}+1 \cdot \frac{1}{8} \\ \sin \theta \cdot \frac{1}{8} + \left(\sin \theta - h \cos \theta\right) \frac{3}{8} + h \cdot \frac{3}{8} + 0 \cdot \frac{1}{8} \end{bmatrix} = \begin{bmatrix} \cos \frac{\theta}{2} \\ \sin \frac{\theta}{2} \end{bmatrix} \end{equation}

对公式(5)进行三角公式变换以及换元,不妨令θ=2α\theta=2\alpha,易得

[cos2α+34hsinαcosαsinαcosα+34hsin2α]=[cosαsinα]\begin{equation} \begin{bmatrix} \cos ^2\alpha + \frac{3}{4}h·\sin\alpha\cos\alpha \\ \sin\alpha\cos\alpha+\frac{3}{4}h·\sin^2\alpha \end{bmatrix} = \begin{bmatrix} \cos \alpha \\ \sin \alpha \end{bmatrix} \end{equation}

化简可得

[cosα34hsinαcosα+34hsinα]=[11]\begin{equation} \begin{bmatrix} \cos \alpha - \frac{3}{4}h·\sin\alpha \\ \cos\alpha+\frac{3}{4}h·\sin\alpha \end{bmatrix} = \begin{bmatrix} 1 \\ 1 \end{bmatrix} \end{equation}

解方程,并带入θ=2α\theta=2\alpha

h=431cosθ2sinθ2\begin{equation} h=\frac{4}{3}\cdot\frac{1-\cos\frac{\theta}{2}}{\sin\frac{\theta}{2}} \end{equation}

以上,就是圆心位于0 0的单位圆的第一象限圆弧的贝塞尔曲线逆向拟合推导过程,以此为结论,通过平移、缩放等变换,可以推导出任意象限任意大小的圆弧对应的贝塞尔曲线的4个点的坐标。

SVG颜色、效果标签

篇幅原因,本期仅对渐变相关标签做介绍

渐变标签

在CSS中,渐变的形式主要有线性渐变linear-gradient、径向渐变radial-gradient和锥形渐变conic-gradient,而svg所表示的渐变则仅有线性渐变和径向渐变,对应的标签分别是 <linearGradient /><radialGradient>,语法分别为:

<!-- cx,cy,r分别为渐变颜色这个椭圆的形状参数,fx、fy是渐变中心的位置参数 -->
<radialGradient cx="" cy="" r="" fx="" fy=""></radialGradient>
<!-- x1 y1 x2 y2为渐变色的起点和终点 -->
<linearGradient x1="" y1="" x2="" y2=""></linearGradient>
一个注意事项

由于svg渐变标签并不能做角度渐变(conic-gradient),而一般设计工具并不会对其作任何限制。

此时如果ui交给你一个loading的icon。

在figma中。其svg代码会被转换成

<svg xmlns="http://www.w3.org/2000/svg" width="213" height="213" viewBox="0 0 213 213" fill="none">
  <path d="M213 106.5C213 165.318 165.318 213 106.5 213C47.6817 213 0 165.318 0 106.5C0 47.6817 47.6817 0 106.5 0C165.318 0 213 47.6817 213 106.5ZM21.5651 106.5C21.5651 153.408 59.5918 191.435 106.5 191.435C153.408 191.435 191.435 153.408 191.435 106.5C191.435 59.5918 153.408 21.5651 106.5 21.5651C59.5918 21.5651 21.5651 59.5918 21.5651 106.5Z" fill="url(#paint0_angular_494_685)"/>
  <defs>
    <radialGradient id="paint0_angular_494_685" cx="0" cy="0" r="1" gradientUnits="userSpaceOnUse" gradientTransform="translate(106.5 106.5) rotate(90) scale(106.5)">
      <stop stop-color="white" stop-opacity="0"/>
      <stop offset="1"/>
    </radialGradient>
  </defs>
</svg>

显然<radialGradient>标签并不能实现这个效果。它渲染出来是这样的

那么应该如何实现这个效果?iconify收集的图标库图标里有这样的实现,他是用两段线性渐变实现的这种近似角度渐变的效果。代码如下

<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24">
  <defs>
    <linearGradient id="mingcuteLoadingFill0" x1="50%" x2="50%" y1="5.271%" y2="91.793%">
      <stop offset="0%" stop-color="currentColor" />
      <stop offset="100%" stop-color="currentColor" stop-opacity="0.55" />
    </linearGradient>
    <linearGradient id="mingcuteLoadingFill1" x1="50%" x2="50%" y1="15.24%" y2="87.15%">
      <stop offset="0%" stop-color="currentColor" stop-opacity="0" />
      <stop offset="100%" stop-color="currentColor" stop-opacity="0.55" />
    </linearGradient>
  </defs>
  <g fill="none">
    <path
      d="m12.593 23.258l-.011.002l-.071.035l-.02.004l-.014-.004l-.071-.035q-.016-.005-.024.005l-.004.01l-.017.428l.005.02l.01.013l.104.074l.015.004l.012-.004l.104-.074l.012-.016l.004-.017l-.017-.427q-.004-.016-.017-.018m.265-.113l-.013.002l-.185.093l-.01.01l-.003.011l.018.43l.005.012l.008.007l.201.093q.019.005.029-.008l.004-.014l-.034-.614q-.005-.018-.02-.022m-.715.002a.02.02 0 0 0-.027.006l-.006.014l-.034.614q.001.018.017.024l.015-.002l.201-.093l.01-.008l.004-.011l.017-.43l-.003-.012l-.01-.01z" />
    <path fill="url(#mingcuteLoadingFill0)"
      d="M8.749.021a1.5 1.5 0 0 1 .497 2.958A7.5 7.5 0 0 0 3 10.375a7.5 7.5 0 0 0 7.5 7.5v3c-5.799 0-10.5-4.7-10.5-10.5C0 5.23 3.726.865 8.749.021"
      transform="translate(1.5 1.625)" />
    <path fill="url(#mingcuteLoadingFill1)"
      d="M15.392 2.673a1.5 1.5 0 0 1 2.119-.115A10.48 10.48 0 0 1 21 10.375c0 5.8-4.701 10.5-10.5 10.5v-3a7.5 7.5 0 0 0 5.007-13.084a1.5 1.5 0 0 1-.115-2.118"
      transform="translate(1.5 1.625)" />
  </g>
</svg>

<stop />标签

stop标签用于决定渐变过程中的颜色节点,基本用法为

<!-- offset为节点的偏移量,stop-color节点的颜色 -->
<stop offset="" stop-color=""/>

应用

那么有人就要问了,搞这么多svg的东西,有什么用呢?

首先最广泛的用途,自然是拿来绘制图形、图表之类的内容。

比如RoughJS这个库就是基于svg封装的一个绘图库(虽然他也支持canvas)

除此之外呢?基于我本期介绍的内容,我总结了两个点。

绘制简单的路径动画

jakearchibald.com/2013/animat…

对于一些路径动画,可以 通过控制 stroke-dasharraystroke-dashoffset 这两个参数实现路径动画效果。

前端资源体积优化

举个例子

在某天,一个落地页需求中,ui给了你这样的一张图(图片仅为案例。实际ui不会出这么丑的图!)

以上图为例,如果以png的方式导出一倍图,二倍图分别为750k和2.5M。

经过tinyPNG压缩后图像大小分别变成了121K和500K。

由于一倍图在较大屏幕上可能会出现失真现象,而二倍图的大小又可能会影响图像加载速度造成不好的体验。那么还有优化空间吗?

为了保证图像不失真,很容易就想到用svg替代png的方式。

于是我们从figma中导出svg,得到了一张体积为88K,可以无线放大不失真的图像。

但是88k的大小似乎还是太大了,还有优化空间吗?

分析svg代码。我们可以看到,图像中的文字都是以路径元素(栅格化文字)呈现的。

<path d="M644.776 68.1338L645.528 67.6538C646.328 68.4538 647.288 69.5258 647.752 70.2138L646.968 70.7578C646.52 70.0538 645.592 68.9338 644.776 68.1338ZM641.976 57.0938H647.384V67.0938H646.408V58.0058H642.936V67.1258H641.976V57.0938ZM650.856 56.3578H651.848V69.4778C651.848 70.1498 651.688 70.4538 651.288 70.6298C650.872 70.7898 650.2 70.8218 649.064 70.8218C649.032 70.5498 648.872 70.1018 648.728 69.8138C649.576 69.8298 650.312 69.8298 650.536 69.8138C650.776 69.8138 650.856 69.7338 650.856 69.4938V56.3578ZM648.664 57.6058H649.624V67.1898H648.664V57.6058ZM644.12 59.1578H645.048V64.8378C645.048 66.9658 644.6 69.3978 641.72 70.8538C641.608 70.6458 641.32 70.2618 641.128 70.1018C643.816 68.7578 644.12 66.7418 644.12 64.8218V59.1578ZM638.296 57.1898L639 56.3258C639.88 56.7898 641.032 57.4938 641.624 57.9898L640.872 58.9658C640.328 58.4378 639.192 57.6858 638.296 57.1898ZM637.592 61.4938L638.28 60.6458C639.16 61.0938 640.328 61.7818 640.936 62.2298L640.232 63.1898C639.656 62.7098 638.488 61.9898 637.592 61.4938ZM637.928 70.0378C638.552 68.8058 639.448 66.7098 640.104 64.9178L641.064 65.5578C640.472 67.2218 639.688 69.1898 639 70.6778L637.928 70.0378ZM654.904 57.1898L655.72 56.4698C656.536 57.1418 657.576 58.1178 658.072 58.7418L657.224 59.5898C656.76 58.9178 655.736 57.9098 654.904 57.1898ZM655.848 70.4698L655.672 69.3978L656.024 68.9018L658.936 67.0298C659.016 67.3338 659.144 67.7978 659.256 68.0378C656.536 69.8938 656.104 70.1658 655.848 70.4698ZM653.8 61.1738H656.632V62.3418H653.8V61.1738ZM658.536 59.4938H668.344V60.6458H658.536V59.4938ZM659.048 62.9658H663.336V64.0858H659.048V62.9658ZM660.712 63.6058H661.816V68.4698L660.712 68.5978V63.6058ZM658.744 68.6298C659.992 68.3098 661.864 67.7978 663.704 67.2858L663.864 68.3418C662.184 68.8538 660.424 69.3658 659.08 69.7498L658.744 68.6298ZM665.432 56.8698L666.248 56.3578C666.92 57.0298 667.688 57.9578 668.024 58.5818L667.144 59.1738C666.824 58.5498 666.088 57.5738 665.432 56.8698ZM663.72 56.2298H664.936C664.984 63.2538 665.656 69.1898 666.92 69.2538C667.208 69.2698 667.4 68.3578 667.496 66.8058C667.72 67.0458 668.248 67.3498 668.456 67.4618C668.136 70.1498 667.512 70.8378 666.888 70.8218C664.472 70.7738 663.896 64.5978 663.72 56.2298ZM655.848 70.4698C655.752 70.1658 655.448 69.6538 655.24 69.4138C655.528 69.2378 656.008 68.7738 656.008 68.1018V61.1738H657.16V69.1418C657.16 69.1418 655.848 70.0218 655.848 70.4698ZM670.328 56.8538H683.656V70.8858H682.456V57.9578H671.496V70.8858H670.328V56.8538ZM671.016 69.1258H683.16V70.1978H671.016V69.1258ZM674.984 65.1418L675.496 64.4058C676.776 64.6618 678.408 65.1898 679.304 65.6058L678.792 66.4058C677.912 65.9738 676.28 65.4138 674.984 65.1418ZM675.624 58.2778L676.616 58.6138C675.704 60.0858 674.248 61.4618 672.904 62.3578C672.728 62.1658 672.312 61.8138 672.072 61.6378C673.432 60.8218 674.808 59.5738 675.624 58.2778ZM679.896 59.5418H680.104L680.296 59.4938L680.968 59.9098C679.32 62.5498 675.592 64.2938 672.344 65.0458C672.232 64.7578 671.992 64.2778 671.784 64.0538C674.92 63.4458 678.52 61.8618 679.896 59.7178V59.5418ZM674.92 60.4218C676.328 62.1178 679.256 63.4138 682.168 63.9418C681.944 64.1658 681.64 64.5658 681.496 64.8538C678.568 64.2298 675.608 62.7578 674.024 60.8378L674.92 60.4218ZM675.256 59.5418H680.248V60.4858H674.616L675.256 59.5418ZM673.4 67.1738L673.96 66.3418C676.12 66.5978 678.872 67.2058 680.44 67.7338L679.896 68.6298C678.376 68.0858 675.608 67.4458 673.4 67.1738ZM692.536 56.1818L693.704 56.3898C692.856 57.8778 691.624 59.5258 689.912 60.8538C689.704 60.6138 689.32 60.3098 689.032 60.1658C690.68 58.9978 691.88 57.4298 692.536 56.1818ZM692.616 57.1898H696.488V58.1178H691.88L692.616 57.1898ZM691.48 60.1978V62.0058H698.584V60.1978H691.48ZM690.376 59.3018H699.752V62.8858H690.376V59.3018ZM694.424 59.6698H695.512V62.3898H694.424V59.6698ZM694.488 62.1018L695.432 62.4858C694.12 63.7018 691.928 64.8058 690.104 65.4618C689.944 65.2538 689.624 64.8378 689.4 64.6298C691.224 64.0858 693.32 63.1578 694.488 62.1018ZM692.808 63.8138L693.592 63.3018C696.648 65.5098 696.728 69.3658 695.352 70.3898C694.888 70.7738 694.456 70.8698 693.816 70.8858C693.48 70.8858 693.032 70.8858 692.568 70.8538C692.568 70.5658 692.472 70.1178 692.28 69.8138C692.792 69.8618 693.272 69.8778 693.592 69.8778C693.992 69.8778 694.28 69.8458 694.568 69.6058C695.528 68.9178 695.448 65.7978 692.808 63.8138ZM694.232 64.4538L694.968 64.9498C693.864 65.9578 691.784 67.1258 690.152 67.7338C690.024 67.4778 689.768 67.1258 689.528 66.9018C691.128 66.4538 693.224 65.3818 694.232 64.4538ZM695.064 66.1658L695.864 66.7418C694.52 68.1338 691.976 69.6858 689.896 70.4698C689.752 70.1818 689.48 69.7978 689.24 69.5738C691.272 68.9338 693.848 67.4938 695.064 66.1658ZM699.368 63.3818L700.216 64.0858C699.384 64.8218 698.264 65.6218 697.416 66.1338L696.712 65.4778C697.544 64.9658 698.712 64.0378 699.368 63.3818ZM696.264 62.2778C696.936 65.3018 698.344 68.0218 700.472 69.2378C700.216 69.4618 699.848 69.8778 699.672 70.1818C697.432 68.7578 696.008 65.8618 695.272 62.4698L696.264 62.2778ZM696.104 57.1898H696.36L696.568 57.1098L697.304 57.6218C696.808 58.5178 696.04 59.4938 695.416 60.1178C695.256 59.9098 694.936 59.5738 694.728 59.4138C695.24 58.8858 695.816 58.0218 696.104 57.3978V57.1898ZM688.712 56.2458L689.832 56.5498C688.92 59.3178 687.496 62.1338 685.912 64.0058C685.8 63.7178 685.48 63.0778 685.272 62.7738C686.68 61.1738 687.944 58.7098 688.712 56.2458ZM687.432 60.1658L688.536 59.0618L688.568 59.0938V70.8698H687.432V60.1658ZM701.96 60.9338C702.035 59.9525 702.413 59.1845 703.096 58.6298C703.779 58.0752 704.664 57.7978 705.752 57.7978C706.477 57.7978 707.101 57.9312 707.624 58.1978C708.157 58.4538 708.557 58.8058 708.824 59.2538C709.101 59.7018 709.24 60.2085 709.24 60.7738C709.24 61.4352 709.048 62.0058 708.664 62.4858C708.291 62.9658 707.8 63.2752 707.192 63.4138V63.4938C707.885 63.6645 708.435 64.0005 708.84 64.5018C709.245 65.0032 709.448 65.6592 709.448 66.4698C709.448 67.0778 709.309 67.6272 709.032 68.1178C708.755 68.5978 708.339 68.9765 707.784 69.2538C707.229 69.5312 706.563 69.6698 705.784 69.6698C704.653 69.6698 703.725 69.3765 703 68.7898C702.275 68.1925 701.869 67.3498 701.784 66.2618H703.192C703.267 66.9018 703.528 67.4245 703.976 67.8298C704.424 68.2352 705.021 68.4378 705.768 68.4378C706.515 68.4378 707.08 68.2458 707.464 67.8618C707.859 67.4672 708.056 66.9605 708.056 66.3418C708.056 65.5418 707.789 64.9658 707.256 64.6138C706.723 64.2618 705.917 64.0858 704.84 64.0858H704.472V62.8698H704.856C705.837 62.8592 706.579 62.6992 707.08 62.3898C707.581 62.0698 707.832 61.5792 707.832 60.9178C707.832 60.3525 707.645 59.8992 707.272 59.5578C706.909 59.2165 706.387 59.0458 705.704 59.0458C705.043 59.0458 704.509 59.2165 704.104 59.5578C703.699 59.8992 703.459 60.3578 703.384 60.9338H701.96ZM711.382 60.9338C711.457 59.9525 711.835 59.1845 712.518 58.6298C713.201 58.0752 714.086 57.7978 715.174 57.7978C715.899 57.7978 716.523 57.9312 717.046 58.1978C717.579 58.4538 717.979 58.8058 718.246 59.2538C718.523 59.7018 718.662 60.2085 718.662 60.7738C718.662 61.4352 718.47 62.0058 718.086 62.4858C717.713 62.9658 717.222 63.2752 716.614 63.4138V63.4938C717.307 63.6645 717.857 64.0005 718.262 64.5018C718.667 65.0032 718.87 65.6592 718.87 66.4698C718.87 67.0778 718.731 67.6272 718.454 68.1178C718.177 68.5978 717.761 68.9765 717.206 69.2538C716.651 69.5312 715.985 69.6698 715.206 69.6698C714.075 69.6698 713.147 69.3765 712.422 68.7898C711.697 68.1925 711.291 67.3498 711.206 66.2618H712.614C712.689 66.9018 712.95 67.4245 713.398 67.8298C713.846 68.2352 714.443 68.4378 715.19 68.4378C715.937 68.4378 716.502 68.2458 716.886 67.8618C717.281 67.4672 717.478 66.9605 717.478 66.3418C717.478 65.5418 717.211 64.9658 716.678 64.6138C716.145 64.2618 715.339 64.0858 714.262 64.0858H713.894V62.8698H714.278C715.259 62.8592 716.001 62.6992 716.502 62.3898C717.003 62.0698 717.254 61.5792 717.254 60.9178C717.254 60.3525 717.067 59.8992 716.694 59.5578C716.331 59.2165 715.809 59.0458 715.126 59.0458C714.465 59.0458 713.931 59.2165 713.526 59.5578C713.121 59.8992 712.881 60.3578 712.806 60.9338H711.382ZM725.332 56.7578H728.116V57.8138H726.308V70.8538H725.332V56.7578ZM727.892 56.7578H728.052L728.18 56.7098L728.916 57.0938C728.596 58.4218 728.164 60.0538 727.7 61.7018C728.596 63.2058 728.98 64.2778 728.98 65.2698C728.98 66.1338 728.804 66.9498 728.42 67.2698C728.244 67.4298 727.988 67.5258 727.716 67.5578C727.492 67.5898 727.156 67.5898 726.868 67.5898C726.852 67.3018 726.756 66.8858 726.596 66.6138C726.868 66.6458 727.14 66.6458 727.332 66.6298C727.492 66.6298 727.636 66.5978 727.732 66.5178C727.94 66.3418 728.004 65.7818 728.004 65.2378C728.004 64.3258 727.652 63.3498 726.772 61.8298C727.252 59.9898 727.636 58.1978 727.892 56.8538V56.7578ZM721.524 57.4298H724.404V66.6138H721.524V65.4458H723.428V58.5978H721.524V57.4298ZM721.028 57.4298H721.972V68.1818H721.028V57.4298ZM729.172 56.9018H735.204V58.0538H729.172V56.9018ZM729.444 59.8778H731.892V60.9658H730.292V67.7178H729.444V59.8778ZM733.412 57.2698H734.404V69.2858C734.404 69.9418 734.276 70.3258 733.828 70.5338C733.396 70.7258 732.66 70.7418 731.556 70.7578C731.524 70.4378 731.332 69.8938 731.156 69.5898C732.052 69.6218 732.852 69.6058 733.108 69.5898C733.348 69.5898 733.412 69.5098 733.412 69.2858V57.2698ZM731.556 59.8778H732.436V66.6138H729.844V65.5258H731.556V59.8778Z" fill="black"/>

于是我们很自然的就能想到,svg本身就有渲染文字的能力,能不能利用文字标签替换这些栅格化文字呢?

我们可以根据栅格化文字的路径的起一个M指令获取到文字的起始点。并用<text>标签替换原来的栅格化文字路径。

于是我们就得到了一张6.5K大小的首屏图片。

后续可以配合svgo的优化和压缩,进一步缩小图片的体积。