第五章 文档结构
5.1 结构与表现
XML的目标之一便是提供一种能将结构从视觉表示中独立出来的方法。
但是不幸的是,关于XML的很多讨论都强调结构而非表现。
我们将通过详细讨论如何在SVG中指定表现来纠正这一错误。
5.2在SVG中使用样式
SVG允许我们使用四种方式指定图形表现方面的信息:内联样式、内部样式表、外部样式表以及表现属性。
5.2.1 内联样式
我们设置style属性的值为一系列视觉属性。
<circle cx="20" cy="20" r="10" style="stroke:black;stroke-width:1.5;fill:blue;fill-opacity:0.6;" />
5.2.2 内部样式表
可以通过一个内部样式表来罗列常用的样式,而无需在每个SVG元素内植入样式。这样可以为所有某一类元素应用样式,也可以使用命名类为特定元素应用样式。
<svg width = "200px" height = "200px" viewBox = "0 0 200 200" xmlns="http://www.w3.org/2000/svg">
<defs>
<style type="text/css"><![CDATA[
circle{
fill:#ffc;
stroke: blue;
stroke-width:2;
stroke-dasharray: 5 3;
}
]]></style>
</defs>
<circle cx="20" cy="20" r="10" />
<circle cx="60" cy="20" r="15" />
<circle cx="20" cy="60" r="10" style="fill:#cfc" />
<circle cx="60" cy="60" r="15" style="stroke-width:1;stroke-dasharray: none;" />
</svg>
效果图:
5.2.3外部样式表
如果想要为多个SVG文档应用一组样式,可以通过为每个SVG元素复制和粘贴内部样式表的方式俩实现。但是如果你希望能统一修改这些文档的样式,则发现这种方式是不切实际的。我们应该吧开始和结束<style>标签(不包括<![CDATA[ ]]> )之间的所有样式信息保存在一个外部文件,然后把其变成一个外部样式表。
XML:
<?xml version="1.0"?>
<?xml-stylesheet href="ext_style.css" type="text/css"?>
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
<svg xmlns="http://www.w3.org/2000/svg" width="200px" height="200px" viewBox="0 0 200 200">
<line x1="10" y1="10" x2="40" y2="10" />
<rect x="10" y="20" width="40" height="30" />
<circle class="yellow" cx="70" cy="20" r="10" />
<polygon class="thick" points="60 50,60 80,90 80" />
<polygon class="thick semiblue" points="100 30,150 30,150 50,130 50" />
</svg>
外部样式表CSS:
*{fill:none;stroke:black;}/* 所有元素默认样式 */
rect{
stroke-dasharray:7 3;
}
circle.yellow{
fill:yellow;
}
.thick{
stroke-width:5;
}
.semiblue{
fill:blue;
fill-opacity:0.5;
}
效果图:
内联样式几乎总是比内部或者外部样式表渲染得更快,这是因为样式表和类增加了渲染时的查询和解析时间,然而样式表更容易管理,更小的文件体积和可缓存的特性可以加快文档加载速度。
5.2.4 表现属性
虽然大多数SVG文档都使用样式来表达表现信息,但是SVG的确允许我们使用表现属性指定这些信息:
<circle cx="10" cy="10" r="5" fill="red" stroke="black" stroke-width="2" />
这样做混合了结构与表现。当我们通过将XML数据源转换为SVG的方式来创建SVG文档时,表现属性会派上用场。在这些情况下,为每个表现属性创建独立的属性,要比为一个style属性创建内容更容易。如果使用SVG的环境下不支持样式表,可能需要使用表现属性。
表现属性位于优先级别列表的最底部。任何来自内联样式、内部样式或者外部样式表的样式声明都会覆盖表现属性,但表现属性会覆盖继承的样式。
强调:指定表现信息的首选应该是使用style属性或者样式表。样式表允许我们为文档中的某些元素应用一系列复杂的填充和笔画特性,而不必为每个元素复制一遍表现信息,这正是表现属性所应该有的效果。样式表的能力和灵活性允许我们花最少的经理就能改变对多个文档的外观。
5.3 分组和引用对象
我们通常认为大多数非抽象的艺术作品都是由一系列命名对象组成的,而这些对象由形状和线条组合而成。SVG提供一些元素,允许我们对元素进行这样的分组,从而使文档更加架构化、更易理解。
5.3.1<g>元素
<g>圆度会将其所有子元素作为一个组合,通常组合还会有一个唯一的id作为名称。每个组合还可以拥有自己的<title>和<desc>来供基于文本的XML应用程序识别。需索SVG渲染代理都会在鼠标悬停或者轻触组合内的图形时显示一个提示框,显示<title>元素的内容。屏幕阅读器也会读取<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>
效果图:
5.3.2 <use>元素
SVG使用<ues>元素,为定义在<g>元素内的组合或者任意独立图形元素提供了类似于复制粘贴的能力。
定义了一组图形对象以后,可以使用<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" />
效果图:
5.3.3<defs>元素
<defs>(定义)元素可以通过在起始和结束<defs>标记之间放置这些组合对象,我们可以告诉SVG只定义但不现实它们。
http://oreillymedia.github.io/svg-essentials-examples/ch05/defs-example.html
<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>
效果图:
<use>元素并不限制只能使用同一文件内的对象,事实上xlink:href属性可以指定任意有效的文件或者URI。这使得我们可以将一组公用元素集合在一个SVG文件内,然后在其他文件中选择性地使用它们。比如嗯嘛可以创建一个名为identity.svg的文件,该文件包含你的组织要使用的所有标识图形:
<g id="company_mascot">
<!-- 绘制企业吉祥物 -->
</g>
<g id="company_logo" style="stroke: none;">
<polygon points="0 20,20 0,40 20,20 40" style="fill:#696;" />
<rect x="7" y="1" width="26" height="26" style="fill:#c9c;" />
</g>
<g id="partner_logo">
<!-- 绘制合作伙伴的logo -->
</g>
然后用如下方式引用它们:
<use xlink:href="identity.svg#company_logo" x="200" y="200" />
出于安全问题,并非所有的SVG阅读器都支持外部引用,尤其是Web浏览器。有些浏览器(尤其是IE)完全不支持外部文件引用。其他浏览器也只允许<use>元素引用同一域下的文件或者专门配置了允许跨域使用的Web服务器上的文件。
5.3.4 <symbol>元素
<symbol>元素提供了另一种组合元素的方式。和<g>元素不同,<symbol>元素永远不会显示。因此我们无需把它放在<defs>规范内。然而,我们仍习惯将它放到<defs>中,因为symbol也是我们定义的供后续使用的元素。symbol还可以指定viewBox和preserveAspectRatio属性,通过给<use>元素添加width和height属性就可以让symbol适配视口大小。
<svg width = "200px" height = "200px" viewBox = "0 0 200 200" xmlns="http://www.w3.org/2000/svg">
<title>Symbols vs.groups</title>
<desc>Use</desc>
<defs>
<g id="octagon" style="stroke: black;">
<desc>Octagon as group</desc>
<polygon points="36 25,25 36,11 36,0 25,0 11,11 0,25 0,36 11" />
</g>
<symbol id="sym-octagon" style="stroke: black;" preserveAspectRatio="xMidYMid slice" viewBox="0 0 40 40">
<desc>Octagon as symbol</desc>
<polygon points="36 25,25 36,11 36,0 25,0 11,11 0,25 0,36 11" />
</symbol>
</defs>
<g style="fill:none;stroke:gray;">
<rect x="40" y="40" width="30" height="30" />
<rect x="80" y="40" width="40" height="60" />
<rect x="40" y="110" width="30" height="30" />
<rect x="80" y="110" width="40" height="60" />
</g>
<use xlink:href="#octagon" x="40" y="40" width="30" height="30" style="fill:#c00;" />
<use xlink:href="#octagon" x="80" y="40" width="40" height="60" style="fill:#cc0;" />
<use xlink:href="#sym-octagon" x="40" y="110" width="30" height="30" style="fill:#cfc;" />
<use xlink:href="#sym-octagon" x="80" y="110" width="40" height="60" style="fill:#699;" />
</svg>
效果图:
5.3.5<image>元素
<use>元素允许我们复用SVG文件的一部分,而<image>元素可以包含一个完整的SVG或者栅格文件。如果包含一个SVG文件,其视口会基于引用文件的x、y、width和height属性来建立。如果包含一个栅格文件,它会被缩放以适配该属性指定的矩形。
以下实例展示如何在SVG文件内包含JPEG文件。
使用<defs>元素:
<svg width = "310px" height = "310px" viewBox = "0 0 310 310" xmlns="http://www.w3.org/2000/svg">
<ellispse cx="154" cy="154" rx="150" ry="120" style="fill:#999999;" /><!-- 创建一个灰色椭圆模拟投影 -->
<ellispse cx="152" cy="152" rx="150" ry="120" style="fill:#cceeff;" /><!-- 创建主蓝色椭圆形。因为它在灰色椭圆之后出现,因此它显示在灰色椭圆之上。 -->
<image xlink:href="kwanghwamun.jpg" x="72" y="92" width="160" height="120" /><!-- 指定要包含文件的URI、图像左上角位置,应该被缩放的宽度和高度 -->
</svg>
如果图像文件的尺寸与元素的宽度和高度不匹配,<image>元素可以使用preserveAspectRatio属性只是浏览器应该怎么处理。其默认值是xMIdYMid meet,这会缩放图像并居中显示在指定的矩形中。如果包含一个SVG文件,还可以在preserveAspectRatio值的开头添加defer关键字(比如defer xMidYMid meet);这样如果包含的图像也有preserveAspectRatio属性,则会使用图像的属性来替代默认值。