深入理解svg
SVG 是 XML(有时也是 HTML)
意味着如果你忘记引入 XML 的命名空间或者 混淆了 XML 语法的重要细节,你的网页将什么都不显示。
然而,当你直接在 HTML5 标记中插入 SVG 时,它将由 HTML 解析器处 理。HTML 解析器会忽略一些错误(比如缺少结束标签或使用不带引号的 属性),而在 XML 解析器(或大多数仅支持 SVG 的图形编辑器)中这将导 致解析失败。它也会忽略自定义的命名空间,将不能识别的属性或标签名 变成小写,甚至引起其他不能预期的变化。
SVG 是可压缩的
在普通的文件服务器上存储压缩过的 SVG 时, 通常使用 .svgz 作为扩展名。
SVG 是有结构的
SVG 是有样式的
还可以使用媒体属性或瞬时状态等有条件的 CSS 样 式,比如 :hover 和 :focus。
浏览器会把 SVG 标记和样式转换成一个文档对象模型(document object model,DOM)。DOM 可以通过 JavaScript 进行操作
SVG 的渲染模型被称为画家模型。就像在墙上涂层,上层的内容会遮盖下 层的内容。
z-index 和 paint-order 这两个允许你改变渲染规则的属性。
2.1 使用 fill 属性进行填充
SVG 代码中的基本元素和属性可以精确地定义几何形状。
它的左上角就是坐标系的原点,代码如下所示: 创建一个直径为 10 厘米的圆,并把它放置在坐标系的中心,代码如下:
SVG 通常会被缩放,所以英尺、厘米等 长度单位不一定符合真实世界的距离。它会受到显示器的 分辨率、浏览器的缩放程度,当然还有 SVG 元素上添加的 viewBox 或 transform 等属性的影响。
SVG 无单位坐标通 常可以和 px 等价使用。
如果在你的 SVG 中仅仅包含一个圆或一个长方形的标记(或其他任何形状 或文本)而没有其他的样式信息,它将在你定义的尺寸内显示一个纯黑色 的区域。这是因为 fill 属性的默认值是纯黑色。
如果你不想让软件填充形状,可以把 fill 属性值设置为 none。
fill 属性(以及 stroke 属性)的最后一个可以设置的值是 currentColor 关键词。这一关键词通常被估算为给定元素的 CSS color 属性的当前值。 color 属性本身对 SVG 没有直接的影响,但是结合 currentColor,它将有 两个主要用途。
• 使内联的 SVG 图标与它周围的 HTML 文本颜色协调。color 属性的主要 用途是设置 CSS 样式文本的颜色。因此,使用 currentColor 值的内联 SVG 图形会继承周围 HTML 标记的文本颜色。 • 为重复使用的内容提供一个间接继承的样式值。使用 元素复制的 SVG 图形可以从使用它的上下文中继承 fill 和 stroke 等样式。给重复 使用的图形中的重要属性使用 currentColor,这样可以通过改变 元素上的 color 值来分开操作复用图形的 fill 和 stroke 的值。
默认情况下,fill 属性被渲染为纯色且不透明(除非在渲染服务中有不同 的指令)。可以通过给 fill-opacity 属性设定值来调整不透明度,它接受一 个小数作为值:0 到 1 之间的值会导致填充值和背景色混合起来渲染图形, 值为 1(默认值)时表示不透明,值为 0 时的效果相当于设置 fill 属性的 值为 none
当你不能确定图形的某一部分是在图形之内还是之外时,fill-rule 属性可 以给计算机发送精确的指令。它会影响内部有洞的 元素以及路径、 多边形和纵横交错的折线。
fill-rule 属性有两个可选值。
• evenodd 值的每一条边缘线都分隔开了图形的内部和外部。 • nonzero 值(默认值)是当你从头到尾沿着一个方向画交叉的边线时企 图得到“更多的内部空间”,并且只有你在图形内部沿着相反方向绘画来 撤销它们时才会返回图形外部。
SVG 中的每个图形和文本都可以被填充,且默认是填充的。这包括不闭合 的 元素和 元素,它们可以定义一个结束点不与起始点 相连的图形
中没有闭合的片段是通过连接到子路径的初始点来闭 合的: 最后的点是通过一个 move-to 命令创建的。
即使是一个 元素严格来说也是默认填充的: 因为连接终点与起始点 的返回线与原线完全重合,所以最后的形状不包括任何区域。形状内是没 有点的,所以没有点被填充值影响。如果你想看到它,就得给它加 stroke。
2.2 使用 stroke 属性描边
描边形状的每个部分都只会绘制一次,无论形状中是否存在 不同边重叠或交叉的部分。
stroke 的默认值是 none,即不渲染描边区域。就像 fill 属性一样,它的值 还可以是色值或渲染服务元素的引用。
stroke-width 描边宽度,即描边的粗细。其值可以是长度值、用户单位数或者坐标系 宽和高的加权百分比。在 SVG 1.1 中,描边区域通常以形状的边为中心, 所以描边的一半宽度在形状之内,一半在形状之外。 stroke-linecap 该属性用来给未闭合的路径或线条设置描边样式。其默认值 butt 会紧密 修剪描边并且与端点垂直。其他选项(round 和 square)会以特定形状 (即分别以半圆形和方形)使用一半的描边宽度来延伸描边。 stroke-linejoin 该属性用于指定在形状中拐角的描边样式。其默认值 miter 在直线上延 伸描边,直到两条边在某一点相汇。其他可选值是 round(使用圆弧来 连接两条描边)和 bevel(使用一根额外的直线连接两条描边)。 stroke-miterlimit 延伸斜接线可以超出形状边线的最大距离,是描边宽度的倍数(默认 是宽度的四倍)。如果描边在这个距离之内没有汇合,则使用 stroke- linejoin 值为 bevel 时的效果。 stroke-dasharray 定义给形状间断描边时的距离模式(线和间隔)。其默认值 none 会给 整个形状添加连续的描边。每一条线的端点都受 stroke-linecap 值的 影响。 stroke-dashoffset 定义间断描边时起始偏移的距离。默认值是 0。
2.3 层叠描边和填充
当一个图形同时拥有填充属性和描边属性时,描边区域和填充区域会有一 部分重合的地方,因此重合部分会有两种特定的颜色。在所有的 SVG 中, 画家模型都适用: 如果两种颜色都是不透明的,则上层的颜色将会替换下 层的颜色。
默认情况下,描边是渲染在填充层之上的。这意味着你通常可以看到整个 描边宽度,也意味着如果描边是半透明的话,将显示出两种颜色。填充的 颜色将会出现在描边区域内部一半之下而不是外部一半之下。
前面的代码片段使用了大量继承的样式。
本身没有设置任何的 fill 和 stroke 属性,而是从它的外层继承的。
总的 stroke 和 fill 属性设置在 外层的 元素上,之后在嵌套的组和 元素上分别抵消了 fill 属性 和 stroke 属性。
在 svg2 中引入了 paint-order 属性。它使用空 格分隔的关键词(stroke、fill 以及 markers)列表来指示图形的各部分被 渲染的顺序。所以可以用单个元素实现相同的效果: 你在 paint-order 属性中没有定义的渲染层将会在之后渲染(本例中的 画家模型 | 15
markers),并按照它们本来的顺序渲染。这意味着如果你想调换 fill 和 stroke 的顺序,仅需要定义 stroke 就可以了。 stroke 将会最先被渲染,然后是 fill,最后是 markers。整个填充区域将 始终可见,即使是与描边重叠的地方。
paint-order 的默认值(等于 fill stroke markers)可以用 normal 关键词 显式地设置。
一种解决方式是使用 CSS 的 @supports 条件规则来控制只有在支持 paint- order 属性时才添加轮廓效果。
样式从表现属性上移到了 style 代码块 中,这样就可以使用 CSS 的条件规则了。基本的样式是在不能控制渲染顺 序的时候提供更细的描边,在 @supports 块中使用粗的描边和 paint-order 属性来覆盖基本样式。
图 2-3 和图 2-5 之间 stroke-width 的值被裁掉一半还多。但 是图 2-5 中描边仅仅略微窄一点,这是因为描边内部的一半 在填充之上是可见的。
如果你不能接受使用 @supports 来改变展现规则,唯一的替代方案就是复 制两个相同的元素,一个用来描边,一个用来填充。
SVG 文档中的层级是按照代码中元素定义的顺序排列的: 形状、文本和图 片都会按照标记定义的确切顺序来分层。在 SVG 1.1 中,改变元素渲染顺 序的唯一方式是改变元素在 DOM 中的顺序。
• 它会迫使你打破内容在逻辑上的分组。例如,如果不使用 元素来把 文本和它描述的图形分组,你通常需要把文本标记移到文件的最后,这 样才不会被其他形状遮挡。 • 你不能通过使用 SMIL 或 CSS 动画来改变看到的层级。你必须通过 JavaScript 来操作 DOM,这可能会带来性能问题或中断用户输入的焦点。
SVG 2 中采用 z-index 来重排 SVG 的层级。它的默认值是 0,我们可以给 画家模型 | 21
它设置一个正值来把该元素放到其他图形前面,或者设置负值来把该元素 放到后面。
2.4 使用渲染提示属性
这些最后的属性被认为是你(SVG 的作者)给浏览器或其他把代码转换成 有色像素的软件的提示。当浏览器必须以某种方式牺牲性能或展现时,它们将告诉浏览器或软件哪个属性是你认为最重要的。这样属性或多或少会 有一致的影响。
shape-rendering 浏览器在屏幕分辨率的限制范围内应该如何调整形状的边缘。它有四个 可选的值。 • auto。默认值。这告诉浏览器选择最佳的优化方式。 • optimizeSpeed。这告诉浏览器快速渲染是最重要的特性(可能因为图 形正在执行动画),图形的边缘可能不会被精确地绘制。不过,细微 的变化可能因浏览器而不同,但大部分情况下它和 auto 的效果是相 同的。 • crispEdges。这告诉浏览器应该把填充和描边区域边缘的对比度最大 化,这通常意味着边缘会在最近的像素的边界形成锯齿,而不是对边 缘像素部分着色(反锯齿)。对于垂直线或水平线,这将创建一个锐 利清晰的图像,但是对于曲线或斜线,结果往往不太令人满意。 • geometricPrecision。这告诉浏览器应该尽可能精确地绘制形状,如 果需要可能会使用反锯齿模式。
text-rendering 浏览器应该如何调整文本中字母的形状以及位置。它也有四个可选值。 • auto(默认值)。 • optimizeSpeed,在大多数浏览器中效果和 auto 相同。对于较大的文本, 可能会关闭文本布局调整(对于大于 20 像素的文本,Firefox 默认使 用易读性调整)。 • optimizeLegibility,这告诉浏览器应该尽可能地调整单个字母的渲 染以及文本字符串的布局来使其更易于阅读。实践中,一些浏览器把 它当作扩大字母间距以及字体文件中指定的不必要连字的暗示。 • geometricPrecision,这告诉浏览器应该把字母当作几何形状来精确 地绘制,而不会根据基于分辨率的字体提示来调整。
color-rendering 浏览器在计算颜色时应该精确到什么程度,尤其在使用混合元素和产生 渐变时。标准的提示关键词有如下几个选项: • auto • optimizeSpeed • optimizeQuality
浏览器目前不会响应该属性设置的任何行为。 image-rendering 当图片显示的大小和图片文件本身定义的像素大小不完全相同时,浏 览器应该如何计算来展现光栅图片。在 SVG 1.1 中,可选的标准值有 auto、optimizeSpeed 和 optimizeQuality。然而在实践中,很明显,对 于创建一个“高质量”缩放图片的方法并不总是一致的。在图片包含尖 锐的边缘时,处理相片的最佳算法可以创建毛玻璃效果。 在 CSS Image Values and Replaced Content Module Level 3 中已经接受了 image-rendering 属 性, 但 废 弃 了 optimizeSpeed 和 optimizeQuality 两 个属性值。optimizeSpeed 属性被一个像素化的值(每一个像素都缩放为 一个正方形)替换。此外还增加了 crisp-edges 选项,用于让边缘更加 平滑并维持高对比度。 在编写本书之时,推荐使用 optimizeQuality 选项来平滑插入照片,在 现有的浏览器中它被默认的 auto 值覆盖。目前正在讨论设置一个单独的 smooth 属性,来和 auto 属性加以区分。
创建颜色
关键词颜色是怎么来的
在早期的 HTML 和 CSS 版本 中引入的简单的颜色关键词集,以及从 Unix 电脑 X11 窗口系统中的 SVG (以及后来的 CSS)开始使用的更广泛的关键词集。两种关键词集在所有
所有的 gray 关键词同样也可以拼写为 grey。这是一个特性, 而不是 bug。
关键词的名称和大多数 CSS 关键词一样,是不区分大小写的。如果你觉得 你的颜色非常强,可以把它们全部大写。大部分官方参考文档都使用小写。
例 3-1 创建一个颜色关键词拼图
3.3 自定义颜色
在 CSS 和 SVG 中使用一组 RGB 值来自定义颜色有两种方式: • 函数表示法,格式为 rgb(red,green,blue) • 十六进制表示法,格式为 #RRGGBB 或 #RGB
在函数表示法中使用的值可以是 0 到 255 的整数或者百分比。但不可以混 合使用整数和百分比,所有的值必须使用同一种类型。
六位的十六进制格式的值同样使用 0~255 的数字,但是必须要转换成十六 进制的数值。
三位的十六进制格式是颜色中每个值的两个十六进制数字相同时的简写。
CSS Color Module Level 3 中介绍了另一种描述颜色的方式,它基于更加常见 的颜色理论而不是 RGB 电脑显示器。
色相 - 饱和度 - 亮度(hue-saturation- lightness,HSL)模型把颜色描述为“纯”色和黑色、白色或灰色的混合方 式。它的三个具体值如下。
色相 使用色轮中的角度定义的纯色,角度为 0 度时是纯红色,60 度时是纯黄 色,120 度时是亮绿色,300 度时是品红色,360 度时又回到了纯红色。 饱和度 混合色中纯色的强度(用于调整亮度),0% 的饱和度会有灰色的色度, 100% 的饱和度是鲜亮的颜色。 亮度 混合色中黑色或者白色的程度,0% 的亮度是纯黑色,100% 的亮度是纯 白色,而 50% 的亮度是最鲜艳的颜色。
与 RGB 值不同,HSL 值通常不唯一,不同的 HSL 值可能组合出相同的颜 色。例如饱和度为 0% 时,不论色相值如何,永远显示灰色。亮度为 100% 时,不论色相和饱和度值如何,永远显示白色。
其他两种颜色模型容易与 HSL 混淆:色相 - 饱和度 - 明度 (hue-saturation-value,HSV) 模 型 和 色 相 - 饱 和 度 - 亮 度 (hue-saturation-luminance,不巧,它也缩写为 HSL)模型。 色相和饱和度的定义是相同的,但第三个参数的值是不可互 换的。如果你想匹配其他绘图软件中定义的颜色,要确保你 们使用的是相同的颜色模型。
把 RGB 转换为 HSL 时: • 饱和度的值可以计算为 100% 减去最小 RGB 通道占最大 RGB 通道的百 分比 S = (1 - min/max) × 100% • 亮度的值是最大颜色通道百分比和最小颜色通道百分比的平均值: 36 | 第 3 章
• 色相的值是由中间值与最大颜色通道的值减去最小通道的值的比例决定 的: H = 0 + 60 × ([G−B]/[R−min]),如果最大值是 R H = 120 + 60 × ([B−R]/[G−min]),如果最大值是 G H = 240 + 60 × ([R−G]/[B−min]),如果最大值是 B
3.4 混合和搭配
sRGB 影响灰色或具有不同亮度(lightness)值的颜色的等级,而亮度 (luminance)的权重只会影响色相的不同。而 sRGB 定义了颜色值和感知亮度之间的曲线(伽马调整)关系。
SVG 使用三个不同的属性来控制基础形状和文本的不透明度:opacity、 fill-opacity 以及 stroke-opacity。
4.1 穿透样式
Web 中的不透明度通常使用一个介于 0.0(不可见)和 1.0(纯色,不透明)。之间的小数来表示。当不透明度是颜色或图像的固有组成部分时,这些数 字也被称为 alpha 值。rgba() 和 hsla() 中的 a 指的都是 alpha 通道。这两个 函数都接收四个参数,而不是通常使用的三个,第四个参数是 0 到 1 之间 的 alpha 值。
opacity 属性应用在设置它的元素上——即使是 组、 或者 元素——并且它不会被继承。最终绘制的效果同样会作用在元素所有的子 内容上,这使得整个元素更加均匀透明。
4.2 其他效果
多层重叠显示的最终颜色需要经过以下几个步骤:首先依据 sRGB 模型对 两种颜色进行缩放,然后获取背景和前景(alpha 值是前景的一个权重)的 加权平均值,最后反转 sRGB 缩放。该计算不会在意背景颜色是单一元素 创建的还是与部分透明元素混合创建的。
这种混合颜色的方法被称为简单 alpha 合成。在许多图形程序中,它也被称 为“normal”混合模式。
组元素或 元素上的 isolation 属性用于限制混合作用的范围。隔离元 素在子内容互相混合之后,再使用自己的混合模式来把子内容的混合结果 与背景相融合。
5 渲染和壁纸
当 fill 或 stroke 的值比单一颜色更加复杂时(transparent 或者其他), SVG 使用一种叫作渲染服务的概念来描述图形是如何被渲染的。
渲染服务通过自身特定的 SVG 元素来定义。
这些元素(渐变和图案)不会 直接创建一个可见图形。它们仅仅用于形状或文本的 fill 和 stroke 属性 中。然而,通过使用 XML 标记来定义渲染服务,它可以有无数种变化:任 何 SVG 图形都可以用于生成 SVG 图案,包括其他图案本身。
所有 SVG 渲染服务的一个主要特点是它们生成的图形内容都在长方形区域 内。有时这可能会有些限制,但也会有一些潜在的性能优化机会。
理解渲染服务(尤其是谈论渐变和图案时)的另一种方式是把渲染内容想 象成一大张墙纸。形状是从大块墙纸中裁剪出来的部分,如图 5-1 所示。
理论上说,“渲染”可以是任何事物:一种单独的颜色、一个或多个渐变、 重复的图案、位图、文本,甚至是其他 SVG 文件。实际上,SVG 1.1 有两 种类型的渲染服务:渐变和重复图案
5.2 标识资源
“服务”意味着它是多个资源的外部引用。理论上说,你可以单独创建一个 文件来包含你所有的渲染服务,并且在 fill 和 stroke 属性中引用它,但是 目前浏览器支持比较差。通常情况下,渲染服务指的是每一个渐变或图案 对象可以给多个 SVG 形状提供渲染(渲染指令)。
一个内部引用,就像 url(#myReference) 这样。井号(#)表示接下来是一 个指向特定元素的标记,事实上井号之前没有任何东西表示浏览器应该在 当前文档中查找该元素。具体而言,它寻找的是一个 id 属性与标记片段相 匹配的元素(即 )。 因此,引用一个 ID 为“customBlue”的渲染服务作为填充值如下所示:
因为 fill 是一个表现属性,所以你还可以在文档中任意位置使用一个
rect { fill: url(#customBlue); }
上面的规则将会设置文档中的所有矩形都用那个渲染服务,前提是该样式 不会被更加具体的 CSS 规则覆盖。
外部样式表中的相对 URL 通常指向 CSS 文件的位置,而不是使用这些样式 的文档的位置。
7.2 对象边界盒
通常你要使用渐变填充的形状不会占据整个坐标系统。
渐变矢量属性中的值默认并不是根据绘制 时使用的局部 坐标系来度量的。相反,它们是基于要填充的形状的对象边界盒创建的新 坐标系来定义的。
每个圆都尽可能地填满它的局部坐标系,但四个角无法填充。渐变的定义方 式没有改变,所以渐变矢量依然是从角到角的。对角矢量的最终结果是渐变 中起始和终止的颜色结点被裁剪掉了。
你可以在 元素上使用 gradientUnits 属性来改变它。
设置 gradientUnits 的值为 userSpaceOnUse 使浏览器在用于绘制要填充的形 状的坐标系内解析你的 x1、y1、x2 和 y2 属性。
CSS 与 SVG 使用关键词来设置 CSS 渐变的位置
CSS 线性渐变的默认方向是从上到下。当然你也 可以改变它的默认方向。它的语法与 SVG 完全不同,且与早期试验性的 CSS 渐变也有相当多的不同;为了尽可能地支持早期的浏览器,你可能需 要使用一个 CSS 预处理程序或脚本来把你的渐变重构为旧的语法。 要想改变 CSS 渐变的方向,需要在函数中颜色结点之前添加一个额外的参 数。要创建整整齐齐从一边到另一边、从一个角到另一个角的渐变,你可 以使用关键词:第一个关键词是 to,接着是 left、right、top 或 bottom 之 一来表示边,也可以是这些关键词的组合(例如 top right)表示角。这些 关键词描述的是渐变的终点(也就是渐变要往哪个方向),起点在相反一侧 或相对的角。 当使用角来表示时,渐变过渡的角度会被调整,所以渐变 50% 偏移位置点 连接着另外的角。虽然规范中实现的方式不同,但它的效果和对象边界盒 单位中角到角的 SVG 渐变效果一样。 例 7-4 直接比较了例 7-2 中 SVG 对角渐变(作为图片嵌入)和一个相似的 CSS 渐变(在网页的 body 上)。CSS 渐变中的 to bottom left,意思是它起 始于右上角。图 7-7 显示了最终效果,包括内嵌的 SVG 渐变。
7.4 渐变,变换
gradientTransform 属性的值是可以控制形状位置和方向的相同变换函数的 列表:
• translate(tx,ty) 仅改变位置而不会扭曲 • scale(s) 或 scale(sx,sy) 放大、缩小、拉伸或翻转成镜像 • rotate(a) 或 rotate(a,cx,cy) 围绕原点或指定点进行旋转 • skewX(a) 或 skewY(a) 沿着轴或其他线倾斜
聚焦未来 使用 CSS 规则来变换渐变 CSS Transforms Module 把 gradientTransform 这个表现属性映射到了新的 CSS 样式变换属性。虽然目前还没有浏览器支持,但在未来,你将可以在 渐变元素上设置变换样式,其效果会与 gradientTransform 属性相
倾斜显然是渐变变换来创建对角渐变的一种方式。旋转是另一种方式。
渐变矢量(初始状态下,沿着每个形状的边界盒的顶部从左到右)是围绕 每个盒子的原点(左上角)来旋转的。把渐变矢量旋转 90 度(如底部中间 的椭圆所示)将会使渐变从上到下改变。
CSS 与 SVG 使用角度定位 CSS 渐变 CSS 渐变同样可以使用旋转角度来定位,但是结果与 SVG 中有所不同。 你可以使用带单位的角度作为 linear-gradient 函数的第一个参数,来代替 方向关键词。由于默认方向是从上到下,角度是相对于朝下的垂直矢量来 计算的,为了创建 SVG 默认的指向右侧的渐变,必须把它设置为 -90 度。
最后更新于
这有帮助吗?