暂无图片
暂无图片
3
暂无图片
暂无图片
暂无图片

SVG里都有什么

麟十一 2021-03-07
1301




写在前面



最近我一直在研究有关矢量图和位图的内容,目前工作已经基本收尾,趁着这个部分还比较熟悉,先写一篇记录一下。

这一篇文章包括三部分,第一部分是矢量图和位图的定义,第二部分展示SVG文件的格式,第三部分详细介绍一下path路径中的一些命令。下一篇文章是配套教程--Python如何解析SVG文件&将SVG文件转换为PNG文件。因为SVG里面的内容真的太多了,本篇只涵盖我研究过的部分,其他部分感兴趣的话在文中的链接内自行查看哦



1. 矢量图和位图



有关矢量图和位图的定义感觉维基百科上面说地更清楚一些。先看矢量图像(Vector Graphics)的定义:

"Vector graphics are computer graphics images that are defined in terms of points on a Cartesian plane, which are connected by lines and curves to form polygons and other shapes. "

位图(Bitmap)或者 栅格图像(Raster Graphics)的定义为:

"A raster graphic or bitmap image is a dot matrix data structure that represents a generally rectangular grid of pixels (points of color)."

矢量图像由有方向的点、曲线和直线构成各种形状,这些点和线的方向决定了图形路径的走向。而位图是一种点阵数据结构,位图也可以叫做点阵图,每一张图片都是由像素构成的。一句话总结,矢量图像基于“形状”,位图图像基于“像素”

我们可以随意挑选一张位图图片检查图像属性,可以发现图片的宽和高都是以像素为单位的,从下图可以看到,该图片宽和高都是320个像素,说明该图片是由320*320=102400个像素点构成的。

图片属性界面

另外,矢量图相较于位图最大的优点是图像的缩小和放大不会影响分辨率。举个例子,下图是一张汉字“一.png”的位图图片:
汉字“一.png”图片

如果将图像放大,可以发现汉字的轮廓是由灰度不同的小方格组成,这些小方格就是形成汉字轮廓的像素点:

汉字“一.png”图片放大

而将“一.svg”图片放大之后就不会有这个问题,汉字轮廓依旧非常清晰,因为矢量图像是不会失真的,拿这张SVG图片来说,不论图像如何变化,汉字轮廓始终都由大小和宽度不会改变的点和线构成:

汉字“一.svg”图片放大

总结一下矢量图和位图的区别:

1.1 图像构成
矢量图基于点和线(或者形状);
位图基于像素

1.2 分辨率
矢量图没有分辨率的概念,图像的放大或缩小不会影响清晰度;
位图的清晰度由分辨率决定,分辨率越大,图像越清晰,图像放大会损失清晰度,俗称图像失真

1.3 相互转换
矢量图转换为位图比较容易,使用Python就可以实现(下一篇会详细说明);
位图转换为矢量图会更加复杂

1.4 图像色彩
矢量图的色彩比较单一,一般用来设计Logo这类不需要太复杂色彩的图片;
位图中可选择的色彩很丰富,每一个像素点都可以拥有RGB三通道颜色,所以位图可以显示出非常逼真的图像
 
1.5 文件大小
矢量图文件的大小与图形复杂度有关,所占空间较小;
由于位图的色彩比较丰富,像素点包含的信息很多,文件所占空间较大

1.6 文件格式
矢量图的常见格式有.svg(Scalable Vector Graphics) .eps(Encapsulated PostScript) .ai(Adobe Illustrator document)等;
位图的常见格式有.png(Portable Network Graphic) .bmp(Bitmap Image File) .jpg/.jpeg(Joint Photographic Experts Group) .gif(Graphics Interchange Format)等;
另外,.pdf(Portable Document Format)既可以是矢量图,也可以是位图


2. 有关SVG 


接下来主要谈一谈SVG类型的图片,SVG(Scalable Vector Graphics)全称是可伸缩矢量图形,贴一个官网链接:https://svgwg.org/,里面内容非常多也非常全面,只不过界面有一些简约:


SVG官网界面

在官网中找到的SVG定义的整合+修改版为:

SVG is an XML language, similar to XHTML, which can be used to describe two-dimensional graphics. SVG allows for three types of graphic objects: vector graphic shapes (e.g., paths consisting of straight lines and curves), images and text. 

简单来说SVG就是一种可以来画二维矢量图的XML语言。它由直线、曲线这些矢量形状、图片和文本共三类图形对象构成。

emmm,那么什么是XML语言?学过HTML的同学肯定会很熟悉,XML(EXtensible Markup Language)可扩展标记语言,它用于标记电子文件而使其具有结构性,XML常被用来标记、传输和存储数据。如果没有学过HTML的话,那么JSON格式的数据大家一定不陌生,JSON和XML属于目前常用的两种数据交换格式,或者说互联网中存储和传输过程中使用的数据格式。(有关JSON数据的读取貌似在后面我也会写一篇文,我想写的也太多了)

为了直观地感受SVG文件格式,先举一个很简单的例子:
<svg version="1.1" width="200" height="200" viewBox="0 0 200 200" xmlns="http://www.w3.org/2000/svg">
  <title>Example</title>
  <circle cx="20" cy="20" r="20" fill="lightblue"></circle>
  <rect x="20" y="20" width="50" height="70" fill="none" stroke="green"></rect>
  <path d="M 50 50 L 150 50 L 100 150 Z" fill="pink" stroke="red" stroke-width="3"></path>
</svg>

将代码复制到文本文件里然后保存为SVG格式,打开后在网页端就能看到我们绘制出的图形:

SVG代码绘制出的图形

从上文代码中就能看出来什么叫做“XML语言使得电子文件具有结构性”。SVG代码由一个一个标签构成,每一个标签包含不同的属性,每一个标签都必须有开启标签和关闭标签。代码总共有6行,包含了5个标签。接下来我们一个一个进行解析:

标签一:<svg version="1.1" width="200" height="200" viewBox="0 0 200 200" xmlns="http://www.w3.org/2000/svg"></svg> SVG代码以<svg>标签开始,<svg>标签也可以被认为是根标签或者根元素。标签中包括version,width,height, viewBox和xmlns共5个属性。分别代表SVG版本,画布宽度,高度,可视窗口大小和SVG命名空间。属性width和height默认单位是px(像素),也可以设置成cm(厘米),只不过要注意二者之间的转换。另外,width和height属性可以用百分比来设置,如果设置为100%,代表图片会填满viewBox设置的可视窗口。

在这里需要说明一下viewBox这个属性,代码中设置的是0 0 200 200,代表可视窗口从左上角坐标(0,0)开始,一直到右下角(200,200)。如果把可视窗口设置为0 0 100 100,就会发现图形只显示了从左上角(0,0)到右下角(100,100)的部分:

修改viewBox后绘制出的图形

标签二:<title>Example</title> 比较简单,标签<title>定义了一个描述性的字符串,这个字符串只能是纯文本。这个title会显示在网页的标题上。


标签三:<circle cx="20" cy="20" r="20" fill="lightblue" stroke="none"></circle> 绘制了一个圆,<circle>标签属性有cx,cy,r,和fill。分别代表圆心的x坐标,y坐标,圆的半径和填充色。如果不设置圆心坐标,则默认为左上角(0,0)。


标签四:<rect x="20" y="20" width="50" height="70" fill="none" stroke="green"></rect> 绘制了一个矩形,<rect>标签包含x,y,width,height,fill和stroke 6个属性,分别代表举行左上角的x坐标,y坐标,举行宽度,高度,填充色和轮廓颜色。和圆相同,如果不设置x和y坐标则默认为(0,0)。另外,fill=“none”代表了无填充色,也可以设置为“transparent”透明填充色。


标签五:<path d="M 50 50 L 150 50 L 100 150 z" fill="pink" stroke="red" stroke-width="3"></path> 则是绘制了一条路径,有关<path>标签的具体内容在后文会详细介绍。这里先介绍属性,包括d,fill,stroke和stroke-width,代表路径,填充色,轮廓颜色和轮廓宽度。


我们从图中还可以发现三个形状是一个叠着一个的,类似于三个图层,后面的形状会覆盖掉前面的形状。另外,SVG文件中还有许多的标签,<path>标签中的属性还可以画出各种各样的图形。除了在刚才提到的官网上可以找到各类标签的详细解释之外,还有一个网站也很推荐,可以选择中文版:https://developer.mozilla.org/en-US/docs/Web/SVG 



2. 有关SVG中的<path>



最后一部分详细介绍一下神奇的<path>标签,这个标签是SVG中功能最强大的一个,除了上文创建的矩形,圆形,还可以在属性d中绘制非常多的形状。那么属性d的值是什么?简单来说,它是由“命令+参数”的序列构成的字符串。接下来就介绍一下属性d中的所有命令。


提前说几个点:所有的命令都有大写和小写之分,大写表示绝对位置,小写代表相对位置;命令最后的参数表示最后要到达的位置;上一个命令结束的位置即为下一个命令开始的位置,下文中会用“当前点”代表这个位置。


3.1 moveto(M/m):


moveto命令代表一条路径的开始,可以认为是在纸上画线时首先点上的一个点,之后的命令和所设置的参数会从这个点开始连接。moveto的参数是(x y)+,代表这个起始点的x和y坐标。参数中的“+”并不需要输入,只是代表这条路径没有结束,还会继续连接到下一个命令下一个点,所有的命令中只有关闭路径参数Z没有“+”


3.2 lineto(L/l):


lineto命令表示从当前点到参数(x,y)点绘制一条直线,它的参数为(x y)+。这里的当前点代表是前一个参数设置的的坐标点。


3.3 horizontal lineto(H/h):


horizontal lineto命令和lineto命令相似,不过它是从当前点到参数x点绘制一条水平线,参数是 x +。 


3.4 vertical lineto(V/v):


vertical lineto命令是从当前点绘制一条垂直线,参数是 y +


在开始下一个命令之前,先介绍一下贝塞尔曲线(Bézier curve),这是一种由两个定点和零至无数个控制点绘制的曲线,属性d中的命令会绘制二次贝塞尔曲线和三次贝塞尔曲线。贴一个网址:https://www.jasondavies.com/animated-bezie,里面包括了贝塞尔曲线的动画,还有相关介绍(下图截取自该网页,从左至右依次是一次、二次、三次和四次贝塞尔曲线的动画):


贝塞尔曲线动画


3.5 quadratic Bézier curveto(Q/q):

quadratic Bézier curveto命令表示从当前点开始,绘制一条二次贝塞尔曲线,绘制二次贝塞尔曲线需要三个点,一个起点,一个终点,还有一个用于绘制曲线的控制点。在这个命令中,参数是(x1 y1 x y)+,当前点是曲线起点,(x1 y1)为控制点,(x y)为曲线终点。

3.6 smooth quadratic Bézier curveto(T/t): 

smooth quadratic Bézier curveto命令是绘制光滑的二次贝塞尔曲线,一般来说这个命令都会跟在quadratic Bézier curveto(Q/q)命令之后,参数只有(x y)+,这里的x和y代表曲线的终点,曲线的控制点会自动取前一条曲线控制点的对称点(相对于上一段曲线的终点)。

举一个例子,用下面的代码绘制两条曲线:
<svg version="1.1" viewBox="-150 -150 1000 1000" xmlns="http://www.w3.org/2000/svg">
<style type="text/css"><![CDATA[
.Connect { stroke:#888888; stroke-width:2 }
.SamplePath {}
    .Point { fill:#888888 }]]></style>
<!--二次贝塞尔曲线-->
<path class="SamplePath" d="M20 100 Q100 300 310 100" fill = "none" stroke="red" stroke-width = "8"/>
<circle class="Point" cx="20" cy="100" r="8"/><!--起点-->
<circle class="Point" cx="100" cy="300" r="8"/><!--控制点-->
<circle class="Point" cx="310" cy="100" r="8"/><!--终点-->
<polyline class="Connect" points="20 100 100 300"/>
<polyline class="Connect" points="310 100 100 300"/>
<!--二次平滑贝塞尔曲线你-->
<path class="SamplePath" d="M20 100 Q100 300 310 100 T500 300" fill="none" stroke="blue" stroke-width = "3"/>
<circle class="Point" cx="520" cy="-100" r="8"/><!--控制点-->
<circle class="Point" cx="500" cy="300" r="8"/><!--终点-->
<polyline class="Connect" points="500 300 520 -100"/>
<polyline class="Connect" points="310 100 520 -100"/>
</svg>

最终画出的曲线如下图所示,其中左边红色的一段是使用命令Q画出的二次贝塞尔曲线,我用三个点标注了起点,控制点和终点。而蓝色的长曲线是在Q命令后又加上了T命令,可以发现曲线自动取了Q命令中控制点的对称点作为新的控制点,Q命令中的终点作为新的起点,T命令中的终点作为终点,画出了两条二次贝塞尔曲线(PS:新的控制点是我自己算出来然后画出来的点):

两条二次贝塞尔曲线(一条Q,一条QT)

3.7 cubic Bézier curveto(C/c):

cubic Bézier curveto命令代表绘制一条三次贝塞尔曲线。与二次贝塞尔曲线不同的是,三次贝塞尔曲线拥有两个控制点。参数为(x1 y1 x2 y2 x y)+,其中(x1 y1)指的是控制点1,(x2 y2)指控制点2,(x y)代表曲线终点,曲线起点为当前点。

3.8 smooth cubic Bézier curveto(S/s):

smooth cubic Bézier curveto命令绘制光滑的三次贝塞尔曲线,和光滑的二次贝塞尔曲线命令相似,该命令的参数是(x2 y2 x y)+,(x2 y2)为曲线第二个控制点,(x y)为曲线终点。曲线起点还是当前点,不过第一个控制点会比较复杂,它与第一条C命令中的第二个控制点关于第一条曲线的终点对称,也就是关于曲线起点对称。

同样,使用下面的代码绘制两条三次贝塞尔曲线:
<svg version="1.1" viewBox="-150 -150 1000 1000" xmlns="http://www.w3.org/2000/svg">
<style type="text/css"><![CDATA[
.Connect { stroke:#888888; stroke-width:2 }
.SamplePath {}
    .Point { fill:#888888 }]]></style>
<!--三次贝塞尔曲线-->
<path class="SamplePath" d="M20 100 C100 200 300 300 400 100" fill = "none" stroke="red" stroke-width = "8"/>
<circle class="Point" cx="20" cy="100" r="8"/><!--起点-->
<circle class="Point" cx="100" cy="200" r="8"/><!--控制点1-->
<circle class="Point" cx="300" cy="300" r="8"/><!--控制点2-->
<circle class="Point" cx="400" cy="100" r="8"/><!--终点-->
<polyline class="Connect" points="20 100 100 200"/>
<polyline class="Connect" points="300 300 400 100"/>
<!--三次平滑贝塞尔曲线你-->
<path class="SamplePath" d="M20 100 C100 200 300 300 400 100 S400 300 500 300" fill="none" stroke="blue" stroke-width = "3"/>
<circle class="Point" cx="500" cy="-100" r="8"/><!--控制点1-->
<circle class="Point" cx="400" cy="300" r="8"/><!--控制点2-->
<circle class="Point" cx="500" cy="300" r="8"/><!--终点-->
<polyline class="Connect" points="400 100 500 -100"/>
<polyline class="Connect" points="400 300 500 300"/>
</svg>


最终画出的曲线如下图所示,左边红色的一段是使用命令C画出的三次贝塞尔曲线,蓝色的长曲线是在C命令后又加上了S命令画出的两条三次贝塞尔曲线曲线,曲线自动取了C命令中控制点2关于曲线终点的对称点作为新的控制点1,C命令中的曲线终点作为新的起点

两条三次贝塞尔曲线(一条C,一条CS)


3.9 elliptical arc(A/a):

elliptical arc命令可以说是最复杂的命令了,它绘制一个圆弧。参数为(rx ry xr laf sf x y)+,看着就累了 这些参数为rx(radius-x)弧线所在椭圆的x半轴长,ry(radius-y)弧线所在椭圆的y半轴长,xr(x-axis-rotaiton)弧线所在椭圆的长轴角度,laf(large-arc-flag)是否选择弧长较长的一段弧线,sf(sweep-flag)是否选择逆时针防线的那一段弧线,x弧线终点x坐标,y弧线终点y坐标。

最难的命令要举最好看的例子,弧线虽然难画,但是可以实现各种曲线,在这里我画了一个桃心,还加入了另一个超有用的标签<animate>显示动画,前方彩色爱心出没

变色小爱心送给你

这是实现代码:其实还挺简单的是不是
<svg version = "1.1" viewBox="0 0 100 100" xmlns="http://www.w3.org/2000/svg">
    <path d="M 10 30 A 20 20 0 0 1 50 30 A 20 20 0 0 1 90 30 Q 90 60 50 90 Q 10 60 10 30 z" fill = "red">
        <animate attributeName="fill" values="red; orange; yellow; green; pink" dur="2s"  repeatCount="indefinite"></animate>
    </path>
</svg>


3.10 closepath(Z/z):

closepath命令表示路径结束,这个命令不需要输入参数,它会自动连接路径的开始点,也就是moveto(M/m)命令设置的坐标点,关闭整条路径


写在最后


其实,这一篇文原本是打算作为下一篇“Python解析SVG文件”的前言部分,我之前打算写的也是有关Python解析的一些内容,但当时觉得把一些简介内容先讲一讲会更好,所以才开始写这一篇。原打算简单写写,结果越写越多,这一篇6000个字累了

虽然看上去比较长,其实这一篇也只是讲了SVG里面非常非常小的一部分。SVG官方文档真的看到眼冒金星,只想感叹懂得还是太少了。当时刚接触SVG的时候更是一团乱麻,不知从何下手,不过慢慢地还是整理得稍微清楚了一点,继续学习吧,顺便抓紧把下一篇写了。我觉得我攒的内容根本写不完了


~END~


文章转载自麟十一,如果涉嫌侵权,请发送邮件至:contact@modb.pro进行举报,并提供相关证据,一经查实,墨天轮将立刻删除相关内容。

评论