SVG精髓第二版(一)

212 阅读9分钟

第二章 在网页中使用SVG

2.1 将SVG作为图像

有两种方法:

(1)将svg包含在 <img>元素内(当图像是基本组成部分时,推荐这种方法)

(2)作为另一个元素的CSS样式属性插入(当图像主要作为装饰时)

无论使用哪种方法,都有局限性,因为SVG代码被转换为栅格图像时,与主页面是分离的,两者无法通信,页面上的样式对SVG无效,并且js也无法感知svg。

<img src="cat.svg"/>

background-image:url("cat.svg")

2.2 将svg作为应用程序

如果需要将外部的svg放到HTML中,而不想收到作为图片嵌入时的种种限制时,可以使用嵌入对象<object>,<object>data表示文件路径,type表示嵌入文件的类型,对于svg,type="images/svg+xml"

object元素必须有起始标签和结束标签,之间的内容会在对象不能被渲染时显示。

与图像不同的是,js可以对svg操作

2.3 使用内联svg

<body>
    <svg>
        <circle cx="125" cy="125" r="100" fill="red"></circle>
    </svg>
</body>

第三章 坐标系统

3.1 视口

svg就是一个无限大的画布,画布区域也称为视口。<svg>元素上的widthheight指定视口大小

3.2 默认用户坐标

x轴向右递增,y轴向下递增

image.png

如果想要在 4cm * 5cm纸上设置一个坐标系统,每个坐标为1/16厘米,我们可以设置viewBox属性,它由四个值组成:最小x坐标,最小y坐标,宽度和高度。

<svg width="4cm" height="5cm" viewBox="0 0 64 80">

3.3 保留宽高比

如果viewBox的比例与视口比例不同,则有三种可能:

  • a. 按较小的尺寸等比例缩放图形,使图形完全填充视口
  • b. 按较大的尺寸等比例缩放图形,病裁减掉超出视口的部分
  • c. 拉伸和压缩绘图以使其恰好填充视口 preserveAspectRatio属性允许我们指定被缩放的图片相对视口的对齐方式,以及希望它适配边缘还是要裁剪

preserveAspectRatio="alignment [meet|slice]"

meet说明符在图形超出视口时候会对图形适当缩小调整适配可用的空间

slice说明符直接裁剪超出视口的部分

总共有9个值可选:

xMinYMinxMinYMidxMinYMax
xMidYMinxMidYMidxMidYMax
xMaxYMinxMaxYMidxMaxYMax

x和y表示对齐的轴线,min,mid,max表示对齐的方式。min是往坐标小的方向对齐;mid居中对齐;max是往坐标大的方向对齐

<svg  width="150px" height="300px">
        <circle cx="200" cy="200" r="200" fill="#fdd" stroke="none"></circle>
</svg>

image.png

在不改变width和height的前提下显示完整的圆形

<svg  width="150px" height="300px" viewBox="0 0 400 400">
        <circle cx="200" cy="200" r="200" fill="#fdd" stroke="none"></circle>
</svg>

image.png

等同于

<svg  width="150px" height="300px" viewBox="0 0 400 400" preserveAspectRatio="xMidYMid meet">
        <circle cx="200" cy="200" r="200" fill="#fdd" stroke="none"></circle>
</svg>
<svg  width="150px" height="300px" viewBox="0 0 400 400" preserveAspectRatio="xMidYMid slice">
        <circle cx="200" cy="200" r="200" fill="#fdd" stroke="none"></circle>
    </svg>

image.png

image.png

<svg  width="45px" height="300px" viewBox="0 0 400 400" preserveAspectRatio="none">
        <circle cx="200" cy="200" r="200" fill="#fdd" stroke="none"></circle>
</svg>

image.png

第四章 基本形状

4.1 线段

<line x1="start-x" y1="start-y" x2="end-x" y2="end-y"/>

4.2 笔画特性

stroke-width:笔画宽度

stroke:笔画颜色,默认值为none

stroke-opacity:透明度,取值0.0-1.0,0代表完全透明,1代表不透明。

stroke-dasharray:定义虚线,由一系列数字构成,代表线的长度和空隙的长度,数字之间使用逗号或空格隔开,数字个数应该为偶数,但如果指定的个数为奇数,svg会重复一次,使得个数为偶数

// 9个像素的虚线,5个像素的空隙
 <svg witdh="200" height="200">
        <line x1="10" y1="10" x2="100" y2="100" stroke="red" stroke-dasharray="9 5"></line>
 </svg>

image.png

// 9个像素的虚线,5个像素的空隙 1个像素的虚线,3个像素的空隙
 <svg witdh="200" height="200">
        <line x1="10" y1="10" x2="100" y2="100" stroke="red" stroke-dasharray="9 5 1 3"></line>
 </svg>

image.png

<svg witdh="200" height="200">
    <line x1="10" y1="10" x2="100" y2="100" stroke="red" stroke-dasharray="9 5 1"></line>
</svg>

等价于

<svg witdh="200" height="200">
    <line x1="10" y1="10" x2="100" y2="100" stroke="red" stroke-dasharray="9 5 1 9 5 1"/>
</svg>

image.png

4.3 矩形

指定左上角的x和y坐标,高度和宽度,全部默认为0,高度和宽度不能为负数,会报错。内部使用fill填充,默认为黑色,指定为none时,不填充颜色,为透明,stroke和之前叙述相同

<svg witdh="200" height="200">
    <rect x="10" y="10" width="100" height="150" />
</svg>

image.png

<svg witdh="200" height="200">
   <rect x="10" y="10" width="100" height="150" fill="blue" stroke="red" stroke-width="50"/>
</svg>

image.png

圆角矩形

指定x方向和y方向的圆角半径,rx最大值为矩形宽度的一半,rx最大值为矩形高度的一半,如果只指定一个,则默rx和ry相等。

border-radius可设置50%,使矩形为一个圆形或者椭圆,在svg中也可以设置百分比数值,但是会被解析成相对视口的宽高百分比。

4.4 圆形椭圆

使用<cricle>来绘制圆形,属性:圆心x,y和半径r,x方向半径cx,y方向半径cy

4.5 多边形

<polygon>绘制任意封闭的图形,需要指定一系列x,y坐标系,使用逗号和空格分隔。不需要指定返回起始点的坐标。

 <svg witdh="200" height="200">
     <polygon points="10,10 50,10 40,30 5,30"></polygon>
 </svg> 

image.png

当多边形彼此交叉时,不容易区分多边形的内部和外部区域。

<svg witdh="200" height="200">
    <polygon points="48 16 16 96 96 48 0 48 80 96" stroke="black" fill="none"></polygon>
</svg>

image.png

svg有两种判断方法判断某个点是否在多线内部,分别是 fill-rule:nonzero和evenodd

evenodd

字面意思是“奇偶”。按照规则,要判断一个点是否在图形内,该点作任意方向的一条射线,然后检测射线与图形路径的交点的数量。如果结果是奇数则认为点在内部,是偶数则认为点在外部。如下

image.png

<svg witdh="200" height="200" fill-rule="evenodd">
    <polygon points="48 16 16 96 96 48 0 48 80 96" stroke="black" fill="none"></polygon>
</svg>

image.png

nonzero

字面意思是非零。按照规则,要判断一个点是否在图形内,从该点作任意方向的一条射线,然后检测射线与图形路径的交点情况。从0开始计数,路径从左向右穿过射线则计数加1,从右向左穿过射线则计数减1。得出计数结果后,如果结果是0,则认为点在图形外部,否则认为在内部

<svg witdh="200" height="200" fill-rule="nonzero">
    <polygon points="48 16 16 96 96 48 0 48 80 96" stroke="black" fill="none"></polygon>
</svg>

image.png

4.6 折线

绘制一系列直线段,但是不希望闭合使用<polyline>,建议将fill设置为none

第5章 文档结构

5.1 分组和引用对象

我们通常认为大多数非抽象的艺术作品都是由一系列命名对象组成的,而这些对象由形状和线条组合而成。SVG提供一些元素,允许我们对元素进行这样的分组,从而使文档更加架构化、更易理解。

5.1.1 <g>

<g>元素将所有的子元素作为一个组合,这个组合可以有一个唯一ID,拥有<title><desc>,来描述信息。

<g>元素可以组合元素,并为他们提供一些注解,这使得我们的文档结构更为清晰。<g>元素还提供了一些书写上的便利。在其实<g>标签中指定的所有样式会应用于组合内的所有子元素。而且组合还可以彼此嵌套。

<svg width = "240px" height = "240px" viewBox = "0 0 240 240" xmlns="http://www.w3.org/2000/svg">
        <title>Grouped Drawing</title>
        <desc>Stick-figure drawings of a house and people</desc>
        <g id="house" style="fill:none;stroke:black;">
            <desc>House with door</desc>
            <rect x="6" y="50" width="60" height="60" />
            <polyline points="6 50,36 9,66 50" />
            <polyline points="36 110,36 80,50 80,50 110" />
        </g>
 
        <g id="man" style="fill:none;stroke: black;">
            <desc>Male human</desc>
            <circle cx="85" cy="56" r="10" />
            <line x1="85" y1="66" x2="85" y2="80" />
            <polyline points="76 104,85 80,94 104" />
            <polyline points="76 70,85 76,94 70" />
        </g>
 
        <g id="woman" style="fill:none;stroke: black;">
            <desc>Female human</desc>
            <circle cx="110" cy="56" r="10" />
            <polyline points="110 66,110 80,100 90,120 90,110 80" />
            <line x1="104" y1="104" x2="108" y2="90" />
            <line x1="112" y1="90" x2="116" y2="104" />
            <polyline points="101 70,110 76,119 70"/>
        </g>
    </svg>

image.png

5.1.2 <use>

<use>元素,为定义在元素内的组合或者任意独立图形元素提供了类似于复制粘贴的能力

定义了一组图形对象以后,可以使用标签再次显示它们。要指定想要重用的组合,给xlink:href属性指定URI即可。同时还要指定x和y的位置以表示组合的(0,0)应该移动到的位置

<svg>
     <use xlink:href="#house" x="70" y="100" />
     <use xlink:href="#woman" x="-80" y="100" />
     <use xlink:href="#man" x="-30" y="100" />
</svg>

image.png

5.1.3 <defs>

前面的例子有三个缺点

(1)复用man和 woman组合时,需要知道原始图像中这些图形的位置,并以此位置作为复用的基础,而不是使用诸如0这样简单的数字。

(2)房子的填充和笔画颜色由原始图形建立,并且不能通过元素覆盖。这意味着我们不能构造一行彩色的房子。

(3)文档中会画出所有的三个元素woman、man和 house。我们并不能将它们单独“存储”下来,然后只绘制一排房子或者只绘制一组人。

<defs>元素可以通过在起始和结束标记之间放置这些组合对象,我们可以告诉SVG只定义但不现实它们,svg规范推荐我们将想要复制的元素放在<defs>元素内。

<svg width = "480px" height = "240px" viewBox = "0 0 480 240" xmlns="http://www.w3.org/2000/svg">
        <title>Grouped Drawing</title>
        <desc>Stick-figure drawings of a house and people</desc>
        <defs>
            <g id="house" style="stroke:black;">
                <desc>House with door</desc>
                <rect x="0" y="41" width="60" height="60" />
                <polyline points="0 41,30 0,60 41" />
                <polyline points="30 101,30 71,44 71,44 101" />
            </g>
 
            <g id="man" style="fill:none;stroke: black;">
                <desc>Male human</desc>
                <circle cx="10" cy="10" r="10" />
                <line x1="10" y1="20" x2="10" y2="44" />
                <polyline points="1 58,10 44,19 58" />
                <polyline points="1 24,10 30,19 24" />
            </g>
 
            <g id="woman" style="fill:none;stroke: black;">
                <desc>Female human</desc>
                <circle cx="10" cy="10" r="10" />
                <polyline points="10 20,10 34,0 44,20 44,10 34" />
                <line x1="4" y1="58" x2="8" y2="44" />
                <line x1="12" y1="44" x2="16" y2="58" />
                <polyline points="1 24,10 30,19 24"/>
            </g>
            <g id="couple">
                <desc>Male and female stick figures</desc>
                <use xlink:href="#woman" x="0" y="0" />
                <use xlink:href="#man" x="25" y="0" />
            </g>
        </defs>
        <!-- 利用组合定义 -->
        <use xlink:href="#house" x="0" y="0" style="fill:#cfc;" />
        <use xlink:href="#couple" x="70" y="40" />
 
        <use xlink:href="#house" x="120" y="0" style="fill:#99f;" />
        <use xlink:href="#couple" x="190" y="40" />
 
        <use xlink:href="#woman" x="0" y="145" />
        <use xlink:href="#man" x="25" y="145" />
        <use xlink:href="#house" x="65" y="105" style="fill:#c00;" />
    </svg>

image.png

<use>元素不限制只能使用同一文件的对象,xlink:href属性可以指定任意有效的文件或者URI。这可以让我们将svg图形集中在一个文件内。

第六章 坐标系统变换

6.1 translate变换

6.2 scale变换

通过缩放坐标系统来让对象显示的比定义的尺寸更大或者更小

// 所有的x和y乘以给定的value
transform="scale(value)"

// x坐标乘以x-value, y坐标乘以y-value
transform="scale(x-value,y-value)"
 <svg width="200" height="200" viewBox="0 0 200 200">
   <g id="test">
       <rect x="10" y="10" width="20" height="20" style="fill:none" stroke="black"></rect>
   </g>
   <use xlink:href="#test" transform="scale(2)"></use>
 </svg>

image.png

先缩放后平移和先平移后缩放不等价

<svg width="200" height="200" viewBox="0 0 200 200">
       <g id="test">
           <rect x="10" y="10" width="20" height="20" style="fill:none" stroke="black"></rect>
       </g>
       <use xlink:href="#test" transform="scale(2) translate(30,20)"></use>
       <rect x="10" y="10" width="20" height="20" style="fill:none" transform="translate(30,20) scale(2)" stroke="red"></rect>
</svg>

image.png

6.3 rotate变换

按照指定角度旋转坐标系,顺时针增加

image.png

<svg width="200" height="200" viewBox="0 0 200 200">
       <g id="test">
           <rect x="10" y="10" width="20" height="20" style="fill:none" stroke="black"></rect>
       </g>
       <use xlink:href="#test" transform="rotate(30)"></use>
</svg>

image.png

否则旋转以原点为中心。 此时可以通过平移+旋转的方式来指定旋转中心: translate(centerX,centerY) rotate(angle) translate(-centerX,-centerY)

但是有个更简单的方式:rotate(angle,centerX,centerY)

第七章 路径

所有的基本形状都是元素的简写方式。

7.1 moveto、lineTo、closepath

每个路径必须以moveto命令开始,简写为M,随后跟逗号或者空格分隔的x,y坐标,用来描述笔的当前位置。

moveto命令后面跟着一个或者多个lineto命令,使用L表示,随后跟逗号或者空格分隔的x,y坐标

<svg width="150px" height="150px" viewBox="0 0 150 150">
<g style="stroke: black; fill: none;">

  <path d="M 10 10 L 100 10"/>
  <path d="M 10, 20  L 100, 20  L  100,50"/>
  <path d="M 40 60 L 10, 60 L 40 42.68
    M 60, 60 L 90 60 L 60, 42.68"/>
</g>
</svg>

image.png

M 40 60 移动笔画到(40,60)L 10 10 绘制一条线到(10,60)

想使用<path>绘制矩形,可以采用绘制四条线方式,也可以先绘制三条线,再使用大写的Z表示closepath命令绘制一条线回到当前子路径的起点

<svg width="150px" height="150px" viewBox="0 0 150 150">
<g style="stroke: black; fill: none;">
  <path d="M 10 10 L 100 10 L 100 100 L 10 100 L 10 10"/>
</g>
</svg>

image.png

<svg width="150px" height="150px" viewBox="0 0 150 150">
<g style="stroke: black; fill: none;">
  <path d="M 10 10 L 100 10 L 100 100 L 10 100 Z"/>
</g>
</svg>

image.png

使用这两种方式绘制图形一个明显的区别。使用Z关闭路径时,开始的线和结束的线会被连接到一起,形成一个连续的形状。

<svg width="150px" height="150px" viewBox="0 0 150 150" stroke-width=8>
<g style="stroke: black; fill: none;">
  <path d="M 10 10 L 100 10 L 100 100 L 10 100 L 10 10"/>
  <path d="M 10 10 L 100 10 L 100 100 L 10 100 Z"/>
</g>
</svg>

image.png

image.png

7.2 相对moveto和lineto

前面的命令都是用大写字母,表示绝对坐标,如果想要使用想相对坐标,可以使用小写字母,坐标会被解析为相对于当前笔画的位置。

<path d="M 10 10 l 90 0 l 0 90 l -90 0 Z"/>

image.png

7.3 路径的快捷方式

<path>有一些快捷方式,让我们尽可能使用少的字节来描绘路径

7.3.1 水平和垂直lineto命令

水平线:H命令加x的绝对坐标,h命令加x的相对坐标

垂直线:V命令加y的绝对坐标,v命令加y的相对坐标

 <path d="M 10 10 H 100 V 100 H 10  Z"/>

image.png

7.3.2 路径快捷方式表示法

1.可以在L后面放多组坐标,来绘制图形,也可以在M后面放多个坐标,除了第一队坐标外,剩下的坐标都会被假设跟在lineto后面

<path d="M 30 30 L 55 5 L 80 30 L 55 55  Z"/>
<path d="M 30 30 L 55 5 80 30 55 55  Z"/>
<path d="M 30 30 55 5 80 30 55 55  Z"/>

image.png

2.所有不必要的空白都可以消除。命令字母后面不需要空白,整数和负数之间不需要空白

<path d="M 0 30 L55 5 L80 30 L55-55Z"/>

7.4 贝塞尔曲线

指定两点和一个控制点生成一个曲线。

image.png

使用Q或者q指定一个二次贝塞尔曲线,后面跟着两组指定的控制点和终点的坐标

<path d="M30 75 Q 240 30, 300 120"/>

image.png

<path d="M 30 100 Q 80 30, 100 100 , 130 60 , 200 80"/>

image.png

这个曲线凹凸不平,不够流畅,因此svg提供了T命令,表示流畅的二次曲线,使新的控制点于上一个控制点相对于当前点中心对称

<path d="M30 100 Q 80 30, 100 100 T 200 80"/>

从 30 100 到 100 100 控制点为 80 80 的曲线,再平滑过渡到 200 80

image.png

单个二次贝塞尔曲线只有一个顶点。三次贝塞尔曲线包含一个拐点(曲线从该点开始,从一个方向往另一个方向弯曲),与二次贝塞尔曲线不同,三次曲线有两个控制点

<path d="M 40 50 C 10 10, 140 10, 110 50"/>

从 40 50 到110 50 的曲线

image.png