跳转至

第7节: SVG:一种场景描述语言

SVG: A Scene Description Language

我们结束这一章的内容,再看一看另一个二维图形系统:SVG,即可缩放矢量图形。到目前为止,我们一直在考虑图形编程 API。而 SVG 则是一种场景描述语言,而不是一种编程语言。编程语言通过程序生成其内容来创建场景,而场景描述语言通过列出其内容来“声明性地”指定场景。由于 SVG 是一种矢量图形语言,场景的内容包括形状、颜色和线宽等属性,以及几何变换。这些大多数对你来说应该很熟悉,但在新的上下文中看到它应该是很有趣的。

SVG 是一种 XML 语言,这意味着它有着非常严格且有些冗长的语法。这可能使得编写它有点烦人,但另一方面,这使得即使你不熟悉语法,也可以阅读和理解 SVG 文档。SVG 可能最初代表“简单”矢量图形,但目前来看它绝不是一种简单的语言。我这里只会涵盖其中的一部分,而且还有很多语言的部分和选项我不会提到。我的目标是介绍场景描述语言的概念,并展示这种语言如何使用本章其余部分所使用的相同基本思想。

SVG 可以用作存储矢量图形图像的文件格式,方式与 PNG 和 JPEG 用于存储像素图像的文件格式类似。这意味着你几乎可以用任何网络浏览器打开 SVG 文件查看图像。通过将其用作 <img> 元素的 src,可以在网页中包含 SVG 图像。这就是本页面上显示的 SVG 示例的方法。由于 SVG 文档是用纯文本编写的,因此你可以使用常规文本编辑器创建 SVG 图像,并且可以通过在文本编辑器中打开它或在网页浏览器中显示图像时查看图像的源代码来读取 SVG 图像的源代码。

We finish this chapter with a look at one more 2D graphics system: SVG, or Scalable Vector Graphics. So far, we have been considering graphics programming APIs. SVG, on the other hand is a scene description language rather than a programming language. Where a programming language creates a scene by generating its contents procedurally, a scene description language specifies a scene "declaratively," by listing its content. Since SVG is a vector graphics language, the content of a scene includes shapes, attributes such as color and line width, and geometric transforms. Most of this should be familiar to you, but it should be interesting to see it in a new context.

SVG is an XML language, which means it has a very strict and somewhat verbose syntax. This can make it a little annoying to write, but on the other hand, it makes it possible to read and understand SVG documents even if you are not familiar with the syntax. It's possible that SVG originally stood for "Simple" Vector Graphics, but it is by no means a simple language at this point. I will cover only a part of it here, and there are many parts of the language and many options that I will not mention. My goal is to introduce the idea of a scene description language and to show how such a language can use the same basic ideas that are used in the rest of this chapter.

SVG can be used as a file format for storing vector graphics images, in much the same way that PNG and JPEG are file formats for storing pixel-based images. That means that you can open an SVG file with almost any web browser to view the image. An SVG image can be included in a web page by using it as the src of an <img> element. That's how the SVG examples on this page are displayed. Since SVG documents are written in plain text, you can create SVG images using a regular text editor, and you can read the source for an SVG image by opening it in a text editor or by viewing the source of the image when it is displayed in a web browser.

2.7.1 SVG Document Structure

SVG Document Structure

一个 SVG 文件,就像任何 XML 文档一样,以一些几乎没有人记住的标准代码开头。它应该只是被复制到一个新文档中。以下是一些代码,可作为本节讨论的 SVG 文档的起始点进行复制(请记住,这些代码只使用了完整 SVG 规范的一个子集):

<?xml version="1.0"?>
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" 
"http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
<svg version="1.1" xmlns="http://www.w3.org/2000/svg" 
        xmlns:xlink="http://www.w3.org/1999/xlink"
        width="4in" height="4in" 
        viewBox="0 0 400 400"
        preserveAspectRatio="xMidYMid">

    <!-- 这里放置场景描述!  -->   

</svg>

前三行表示这是一个 XML SVG 文档。文档的其余部分是一个 <svg> 元素,它作为整个场景描述的容器。你需要了解一些关于 XML 语法的知识。首先,XML 元素的一般形式如下:

<elementname attrib1="value1" attrib2="value2">
    ...content... 
</elementname>

元素以一个“开始标签”开始,它以一个“<”开头,后面跟着一个标识符,该标识符是标签名称,并以“>”结尾。开始标签可以包含“属性”,属性的形式为 name="value"。名称是一个标识符;值是一个字符串。值必须用单引号或双引号括起来。元素以一个“结束标签”结束,结束标签的元素名称与开始标签中的元素名称匹配,并且形式为 </elementname>。元素名称和属性名称区分大小写。开始标签和结束标签之间是元素的“内容”。内容可以包含文本和嵌套元素。如果一个元素没有内容,你可以将开始标签末尾的“>”替换为“/>”,并省略结束标签。这被称为“自闭合标签”。例如,

<circle cx="5" cy="5" r="4" fill="red"/>

这是一个实际的 SVG 元素,指定了一个圆。很容易忘记自闭合标签末尾的“/”,但它必须存在以形成合法的 XML 文档。

回顾一下 SVG 文档,以 <svg 开头的五行只是一个长的开始标签。你可以像示例中那样使用标签,并自定义宽度、高度、viewBox 和 preserveAspectRatio 属性的值。下一行是一个注释;XML 中的注释以“<!--”开始,以“-->”结束。

<svg> 标签的 widthheight 属性指定了图像的自然或首选大小。它可以被强制改变大小,例如,如果它被用在一个网页上的 <img> 元素中,该元素指定了不同的宽度和高度。大小可以使用诸如 in(英寸)、cm(厘米)和 px(像素)之类的单位进行指定,每英寸有 90 像素。如果未指定单位,则使用像素。数字和单位之间不能有任何空格。

viewBox 属性设置了用于绘制图像的坐标系。这就是我在 2.3.1 小节 中称之为视窗的东西。viewBox 的值是一个包含四个数字的列表,分别给出了视窗的最小 x 值、最小 y 值、宽度和高度。宽度和高度必须是正值,因此 x 从左到右增加,y 从上到下增加。列表中的四个数字可以用空格或逗号分隔;这在 SVG 中的数字列表中是典型的。

最后,preserveAspectRatio 属性告诉当 viewBox 的宽高比与图像显示的矩形的宽高比不匹配时会发生什么。默认值“xMidYMid”会水平或垂直地扩展 viewBox 的限制以保持宽高比,并且 viewBox 会出现在显示矩形的中心。如果你希望你的图像拉伸以填满显示矩形,忽略宽高比,将 preserveAspectRatio 的值设置为“none”。(宽高比问题已在 2.3.7 小节 中讨论过。)

让我们看一个绘制几个简单形状的完整 SVG 文档。下面是文档。即使你不了解 SVG 的更多信息,你也可以大致猜出它绘制了什么:

<?xml version="1.0"?>
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" 
"http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
<svg version="1.1" xmlns="http://www.w3.org/2000/svg" 
    xmlns:xlink="http://www.w3.org/1999/xlink"
    width="300px" height="200px" 
    viewBox="0 0 3 2"
    preserveAspectRatio="xMidYMid">

<rect x="0" y="0" width="3" height="2" 
                            stroke="blue" fill="none" stroke-width="0.05"/>
<text x="0.2" y="0.5" font-size="0.4" fill="red">Hello World!</text>
<line x1="0.1" y1="0.7" x2="2.9" y2="0.7" stroke-width="0.05

" stroke="blue"/>
<ellipse cx="1.5" cy="1.4" rx=".6" ry=".4" fill="rgb(0,255,180)"/>
<circle cx="0.4" cy="1.4" r="0.3" 
                    fill="magenta" stroke="black" stroke-width="0.03"/>
<polygon points="2.2,1.7 2.4,1 2.9,1.7" 
                    fill="none" stroke="green" stroke-width="0.02"/>

</svg>

这是该示例生成的图像:

pixel-coordinates

对于此示例的绘图坐标系,x 范围从 0 到 3,y 范围从 0 到 2。所有用于绘制的值,包括描边宽度和字体大小,都是以这个坐标系来给出的。请记住,你可以使用任何你觉得方便的坐标系!顺便提一下,未被绘制形状覆盖的图像部分将是透明的。

这是另一个示例,其中包含多种形状。此示例的源代码有很多注释。它使用了我们将在本节的其余部分中讨论的特性。

pixel-coordinates

你可以查看源代码,svg/svg-starter.svg。(例如,在文本编辑器中打开它,或者在网页浏览器中打开它并使用浏览器的“查看源代码”命令。)

An SVG file, like any XML document, starts with some standard code that almost no one memorizes. It should just be copied into a new document. Here is some code that can be copied as a starting point for SVG documents of the type discussed in this section (which, remember use only a subset of the full SVG specification):

<?xml version="1.0"?>
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" 
"http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
<svg version="1.1" xmlns="http://www.w3.org/2000/svg" 
        xmlns:xlink="http://www.w3.org/1999/xlink"
        width="4in" height="4in" 
        viewBox="0 0 400 400"
        preserveAspectRatio="xMidYMid">

    <!-- The scene description goes here!  -->   

</svg>

The first three lines say that this is an XML SVG document. The rest of the document is an <svg> element that acts as a container for the entire scene description. You'll need to know a little about XML syntax. First, an XML "element" in its general form looks like this:

<elementname attrib1="value1" attrib2="value2">
    ...content... 
</elementname>

The element starts with a "start tag," which begins with a "<" followed by an identifier that is the name of the tag, and ending with a ">". The start tag can include "attributes," which have the form name="value". The name is an identifier; the value is a string. The value must be enclosed in single or double quotation marks. The element ends with an "end tag," which has an element name that matches the element name in the start tag and has the form </elementname>. Element names and attribute names are case-sensitive. Between the start and end tags comes the "content" of the element. The content can consist of text and nested elements. If an element has no content, you can replace the ">" at the end of the start tag with "/>", and leave out the end tag. This is called a "self-closing tag." For example,

<circle cx="5" cy="5" r="4" fill="red"/>

This is an actual SVG element that specifies a circle. It's easy to forget the "/" at the end of a self-closing tag, but it has to be there to have a legal XML document.

Looking back at the SVG document, the five lines starting with <svg are just a long start tag. You can use the tag as shown, and customize the values of the width, height, viewBox, and preserveAspectRatio attributes. The next line is a comment; comments in XML start with "<!--" and end with "-->".

The width and height attributes of the <svg> tag specify a natural or preferred size for the image. It can be forced into a different size, for example if it is used in an <img> element on a web page that specifies a different width and height. The size can be specified using units of measure such as in for inches, cm for centimeters, and px, for pixels, with 90 pixels to the inch. If no unit of measure is specified, pixels are used. There cannot be any space between the number and the unit of measure.

The viewBox attribute sets up the coordinate system that will be used for drawing the image. It is what I called the view window in Subsection 2.3.1. The value for viewBox is a list of four numbers, giving the minimum x-value, the minimum y-value, the width, and the height of the view window. The width and the height must be positive, so x increases from left-to-right, and y increases from top-to-bottom. The four numbers in the list can be separated either by spaces or by commas; this is typical for lists of numbers in SVG.

Finally, the preserveAspectRatio attribute tells what happens when the aspect ratio of the viewBox does not match the aspect ratio of the rectangle in which the image is displayed. The default value, "xMidYMid", will extend the limts on the viewBox either horizontally or vertically to preserve the aspect ratio, and the viewBox will appear in the center of the display rectangle. If you would like your image to stretch to fill the display rectangle, ignoring the aspect ratio, set the value of preserveAspectRatio to "none". (The aspect ratio issue was discussed in Subsection 2.3.7.)

Let's look at a complete SVG document that draws a few simple shapes. Here's the document. You could probably figure out what it draws even without knowing any more about SVG:

<?xml version="1.0"?>
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" 
"http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
<svg version="1.1" xmlns="http://www.w3.org/2000/svg" 
    xmlns:xlink="http://www.w3.org/1999/xlink"
    width="300px" height="200px" 
    viewBox="0 0 3 2"
    preserveAspectRatio="xMidYMid">

<rect x="0" y="0" width="3" height="2" 
                            stroke="blue" fill="none" stroke-width="0.05"/>
<text x="0.2" y="0.5" font-size="0.4" fill="red">Hello World!</text>
<line x1="0.1" y1="0.7" x2="2.9" y2="0.7" stroke-width="0.05" stroke="blue"/>
<ellipse cx="1.5" cy="1.4" rx=".6" ry=".4" fill="rgb(0,255,180)"/>
<circle cx="0.4" cy="1.4" r="0.3" 
                    fill="magenta" stroke="black" stroke-width="0.03"/>
<polygon points="2.2,1.7 2.4,1 2.9,1.7" 
                    fill="none" stroke="green" stroke-width="0.02"/>

</svg>

and here's the image that is produced by this example:

pixel-coordinates

In the drawing coordinate system for this example, x ranges from 0 to 3, and y ranges from 0 to 2. All values used for drawing, including stroke width and font size, are given in terms of this coordinate system. Remember that you can use any coordinate system that you find convenient! Note, by the way, that parts of the image that are not covered by the shapes that are drawn will be transparent.

Here's another example, with a larger variety of shapes. The source code for this example has a lot of comments. It uses features that we will discuss in the remainer of this section.

pixel-coordinates

You can take a look at the source code, svg/svg-starter.svg. (For example, open it in a text editor, or open it in a web browser and use the browser's "view source" command.)

2.7.2 Shapes, Styles, and Transforms

Shapes, Styles, and Transforms

SVG 中,基本形状由一个元素来指定,其中标签名称给出形状,属性给出形状的属性。有一些属性用于指定几何形状,比如线的端点或圆的半径。其他属性指定样式属性,比如填充颜色和线宽。(在本书的其他地方,我称样式属性为属性;在本节中,我在其 XML 意义上使用术语“属性”)。还有一个 transform 属性,可以用来对形状应用几何变换。

举个详细的例子,考虑 rect 元素,它指定了一个矩形。矩形的几何形状通常由名为 x、y、width 和 height 的属性给出。x 和 y 的默认值是零;也就是说,它们是可选的,将它们省略与将它们的值设置为零是一样的。widthheight 是必需的属性。它们的值必须是非负的。例如,元素

<rect width="3" height="2"/>

指定一个左上角为 (0,0),宽为 3,高为 2 的矩形,而

<rect x="100" y="200" height="480" width="640"/>

给出一个左上角为 (100,200),宽为 640,高为 480 的矩形。(顺便提一下,XML 元素中的属性可以以任何顺序给出。)rect 元素还有可选属性 rxry,它们可以用来创建“圆角矩形”,其角被椭圆弧替换。rxry 的值给出了椭圆弧的水平和垂直半径。

样式属性可以添加来指定形状的描边和填充方式。默认情况下,使用黑色填充和无描边。(更准确地说,正如我们后面将看到的,形状默认从其环境中继承样式属性的值。黑色填充和无描边是初始环境。)这里是一些常见的样式属性:

  • fill — 指定如何填充形状。该值可以是“none”,表示不填充形状。它可以是颜色,格式与 HTML 画布 API 中使用的 CSS 颜色相同。例如,它可以是常见的颜色名称如“black”或“red”,或者是 RGB 颜色如“rgb(255,200,180)” 。还有渐变和图案填充,不过我这里不会讨论它们。
  • stroke — 指定如何描边形状,可取与“fill”相同的值。
  • stroke-opacityfill-opacity — 是介于 0.0 和 1.0 之间的数字,指定描边和填充的不透明度。小于 1.0 的值会产生半透明的描边或填充。默认值为 1.0,表示完全不透明。
  • stroke-width — 是一个数字,用于设置描边的线宽。请注意,线宽受到变换的影响。默认值是“1”,如果坐标系使用像素作为单位,则这个值是可以接受的,但在自定义坐标系中通常太宽了。
  • stroke-linecap — 确定描边的端点外观。该值可以是“square”、“round”或“butt”。默认值是“butt”。(有关线端点和连接处的讨论,请参见 2.2.1 小节。)
  • stroke-linejoin — 确定描边的两个线段相交处的外观。该值可以是“miter”、“round”或“bevel”。默认值是“miter”。

作为一个使用了许多这些选项的示例,让我们创建一个正方形,其角被圆化而不是尖锐化,大小为 1,居中于原点,并使用半透明的红色填充和灰色描边:

<rect x="-0.5" y="-0.5" width="1" height="1" 
        rx="0.1" ry="0.1"
        fill="red" fill-opacity="0.5"
        stroke="gray" stroke-width="0.05" stroke-linejoin="round"/>

以及一个简单的矩形轮廓,没有填充:

<rect width="200" height="100" stroke="black" fill="none"/>

transform 属性可以用来对形状应用一个或一系列变换。举个例子,我们可以让一个矩形与水平线倾斜 30 度:

<rect width="100" height="50" transform="rotate(30)"/>

值"rotate(30)"表示围绕原点 (0,0) 旋转 30 度(不是弧度!)。正旋转方向通常将正 x 轴旋转到正 y 轴的方向。你可以通过为 rotate 添加参数来指定不同的旋转中心。例如,要围绕相同的矩形中心旋转:

<rect width="100" height="50" transform="rotate(30,50,25)"/>

平移和缩放的工作方式与您可能期望的一样,使用形式为 "translate(dx,dy)" 和 "scale(sx,sy)" 的 transform 值。还有剪切变换,但它们的名称为 skewXskewY,参数是倾斜角而不是剪切量。例如,transform "skewX(45)" 将 y 轴倾斜 45 度,等价于 x 方向的剪切,剪切因子为 1。(将 y 轴倾斜的函数称为 skewX,因为它修改或倾斜了点的 x 坐标,而保持它们的 y 坐标不变。)例如,我们可以使用 skewX 将一个矩形倾斜,并将其变成平行四边形:

<rect width="100" height="50" transform="skewX(-30)"/>

我使用了 -30 度的角度,以便在常规像素坐标系统中使矩形向右倾斜。

transform 属性的值可以是一系列用空格或逗号分隔的变换。与通常相反的顺序应用这些变换到对象上,与它们被列出的顺序相反。所以,

<rect width="100" height="50" 
        transform="translate(0,50) rotate(45) skewX(-30)"/>

首先将矩形倾斜成平行四边形,然后围绕原点旋转平行四边形 45 度,最后沿 y 方向平移 50 个单位。


除了矩形外,SVG 还有线条、圆、椭圆和文本作为基本形状。以下是一些详细信息。一个 <line> 元素表示一条线段,具有几何属性 x1、y1、x2 和 y2 来指定线段端点的坐标。这四个属性的默认值为零,这样更容易指定水平和垂直线。例如,

<line x1="100" x2="300" stroke="black"/>

如果没有 stroke 属性,你就看不到这条线,因为 stoke 的默认值是“none”。

对于 <circle> 元素,几何属性是 cx、cy 和 r,给出圆的中心坐标和半径。中心坐标的默认值为零。对于 <ellipse> 元素,属性是 cx、cy、rx 和 ry,其中 rx 和 ry 给出椭圆在 x 和 y 方向的半径。

一个 <text> 元素略有不同。它有属性 x 和 y,默认值为零,用于指定文本的基点位置。然而,文本本身是作为元素的内容而不是属性给出的。也就是说,该元素分为开始标记和结束标记,文本出现在开始和结束标记之间。例如,

<text x="10" y="30">这段文本将出现在图像中 </text>

通常的描边和填充属性适用于文本,但文本有额外的样式属性。font-family 属性指定字体本身。它的值可以是一种通用字体名称 "serif"、"sans-serif"、"monospace",或系统中可用的特定字体名称。font-size 可以是一个给出字符在坐标系中(近似)高度的数字。(字体大小受坐标和建模变换的影响,就像任何其他长度一样。)通过将 font-weight 设置为 "bold" 和 font-style 设置为 "italic",你可以获得粗体和斜体文本。以下是一个使用了所有这些选项,并施加了一些额外样式和变换的示例:

<text x="10" y="30" 
    font-family="sans-serif" font-size="50" 
    font-style="italic" font-weight="bold"
    stroke="black" stroke-width="1" fill="rgb(255,200,0)"
    transform="rotate(20)">Hello World</text>

In SVG, a basic shape is specified by an element in which the tag name gives the shape, and attributes give the properties of the shape. There are attributes to specify the geometry, such as the endpoints of a line or the radius of a circle. Other attributes specify style properties, such as fill color and line width. (The style properties are what I call attributes elsewhere in this book; in this section, I am using the term "attribute" in its XML sense.) And there is a transform attribute that can be used to apply a geometric transform to the shape.

For a detailed example, consider the rect element, which specifies a rectangle. The geometry of the rectangle is given by attributes named x, y, width and height in the usual way. The default value for x and y is zero; that is, they are optional, and leaving them out is the same as setting their value to zero. The width and the height are required attributes. Their values must be non-negative. For example, the element

<rect width="3" height="2"/>

specifies a rectangle with corner at (0,0), width 3, and height 2, while

<rect x="100" y="200" height="480" width="640"/>

gives a rectangle with corner at (100,200), width 640, and height 480. (Note, by the way, that the attributes in an XML element can be given in any order.) The rect element also has optional attributes rx and ry that can be used to make "roundRects," with their corners replaced by elliptical arcs. The values of rx and ry give the horizontal and vertical radii of the elliptical arcs.

Style attributes can be added to say how the shape should be stroked and filled. The default is to use a black fill and no stroke. (More precisely, as we will see later, the default is for a shape to inherit the values of style attributes from its environment. Black fill and no stroke is the initial environment.) Here are some common style attributes:

  • fill — specifies how to fill the shape. The value can be "none" to indicate that the shape is not filled. It can be a color, in the same format as the CSS colors that are used in the HTML canvas API. For example, it can be a common color name such as "black" or "red", or an RGB color such as "rgb(255,200,180)". There are also gradient and pattern fills, though I will not discuss them here.
  • stroke — specifies how to stroke the shape, with the same possible values as "fill". stroke-opacity and fill-opacity — are numbers between 0.0 and 1.0 that specify the opacity of the stroke and fill. Values less than 1.0 give a translucent stroke or fill. The default value, 1.0, means fully opaque.
  • stroke-width — is a number that sets the line width to use for the stroke. Note that the line width is subject to transforms. The default value is "1", which is fine if the coordinate system is using pixels as the unit of measure, but often too wide in custom coordinate systems.
  • stroke-linecap — determines the appearance of the endpoints of a stroke. The value can be "square", "round", or "butt". The default is "butt". (See Subsection 2.2.1 for a discussion of line caps and joins.)
  • stroke-linejoin — determines the appearance of points where two segments of a stroke meet. The values can be "miter", "round", or "bevel". The default is "miter".

As an example that uses many of these options, let's make a square that is rounded rather than pointed at the corners, with size 1, centered at the origin, and using a translucent red fill and a gray stroke:

<rect x="-0.5" y="-0.5" width="1" height="1" 
        rx="0.1" ry="0.1"
        fill="red" fill-opacity="0.5"
        stroke="gray" stroke-width="0.05" stroke-linejoin="round"/>

and a simple outline of a rectangle with no fill:

<rect width="200" height="100" stroke="black" fill="none"/>

The transform attribute can be used to apply a transform or a series of transforms to a shape. As an example, we can make a rectangle tilted 30 degrees from the horizontal:

<rect width="100" height="50" transform="rotate(30)"/>

The value "rotate(30)" represents a rotation of 30 degrees (not radians!) about the origin, (0,0). The positive direction of rotation, as usual, rotates the positive x-axis in the direction of the positive y-axis. You can specify a different center of rotation by adding arguments to rotate. For example, to rotate the same rectangle about its center

<rect width="100" height="50" transform="rotate(30,50,25)"/>

Translation and scaling work as you probably expect, with transform values of the form "translate(dx,dy)" and "scale(sx,sy)". There are also shear transforms, but they go by the names skewX and skewY, and the argument is a skew angle rather than a shear amount. For example, the transform "skewX(45)" tilts the y-axis by 45 degrees and is equivalent to an x-shear with shear factor 1. (The function that tilts the y-axis is called skewX because it modifies, or skews, the x-coordinates of points while leaving their y-coordinates unchanged.) For example, we can use skewX to tilt a rectangle and make it into a parallelogram:

<rect width="100" height="50" transform="skewX(-30)"/>

I used an angle of -30 degrees to make the rectangle tilt to the right in the usual pixel coordinate system.

The value of the transform attribute can be a list of transforms, separated by spaces or commas. The transforms are applied to the object, as usual, in the opposite of the order in which they are listed. So,

<rect width="100" height="50" 
        transform="translate(0,50) rotate(45) skewX(-30)"/>

would first skew the rectangle into a parallelogram, then rotate the parallelogram by 45 degrees about the origin, then translate it by 50 units in the y-direction.


In addition to rectangles, SVG has lines, circles, ellipses, and text as basic shapes. Here are some details. A <line> element represents a line segement and has geometric attributes x1, y1, x2, and y2 to specify the coordinates of the endpoints of the line segment. These four attributes have zero as default value, which makes it easier to specify horizontal and vertical lines. For example,

<line x1="100" x2="300" stroke="black"/>

Without the stroke attribute, you wouldn't see the line, since the default value for stoke is "none".

For a <circle> element, the geometric attributes are cx, cy, and r giving the coordinates of the center of the circle and the radius. The center coordinates have default values equal to zero. For an <ellipse> element, the attributes are cx, cy, rx, and ry, where rx and ry give the radii of the ellipse in the x- and y-directions.

A <text> element is a little different. It has attributes x and y, with default values zero, to specify the location of the basepoint of the text. However, the text itself is given as the content of the element rather than as an attribute. That is, the element is divided into a start tag and an end tag, and the text that will appear in the drawing comes between the start and end tags. For example,

<text x="10" y="30">This text will appear in the image </text>

The usual stroke and fill attributes apply to text, but text has additional style attributes. The font-family attribute specifies the font itself. Its value can be one of the generic font names "serif", "sans-serif", "monospace", or the name of a specific font that is available on the system. The font-size can be a number giving the (approximate) height of the characters in the coordinate system. (Font size is subject to coordinate and modeling transforms like any other length.) You can get bold and italic text by setting font-weight equal to "bold" and font-style equal to "italic". Here is an example that uses all of these options, and applies some additional styles and a transform for good measure:

<text x="10" y="30" 
    font-family="sans-serif" font-size="50" 
    font-style="italic" font-weight="bold"
    stroke="black" stroke-width="1" fill="rgb(255,200,0)"
    transform="rotate(20)">Hello World</text>

2.7.3 Polygons and Paths

Polygons and Paths

SVG 具有一些很好的功能,可以制作更复杂的形状。<polygon> 元素使得从一系列坐标对创建多边形变得容易。例如,

<polygon points="0,0 100,0 100,75 50,100 0,75"/>

创建了一个五边形,顶点分别在 (0,0)、(100,0)、(100,75)、(50,100) 和 (0,75) 处。points 属性中的每一对数字指定一个顶点。数字可以用空格或逗号分隔。我在这里使用了混合的空格和逗号,以清楚地显示数字的配对。当然,你可以给多边形元素添加通常的描边和填充样式属性。<polyline> 类似于 <polygon>,不同之处在于它将最后一个线段从最后一个顶点返回到起始顶点的部分省略了。这种差异只在描边时才会显示出来;填充多边形时,会将缺失的边添加进去。

<path> 元素更加有趣。事实上,除了文本之外,所有其他基本形状都可以用路径元素制作。路径可以由直线段、贝塞尔曲线和椭圆弧组成(尽管我不会在这里讨论椭圆弧)。指定路径的语法非常简洁,它具有一些我们之前未见过的特性。路径元素具有一个名为 d 的属性,其中包含路径的数据。数据由一个或多个命令组成,每个命令由一个单独的字母后跟命令所需的任何数据组成。你已经熟悉的 moveTo、lineTo、cubic Bezier 和 quadratic Bezier 命令由字母 M、L、C 和 Q 编码。关闭路径段的命令是 Z,它不需要数据。例如,路径数据 "M 10 20 L 100 200" 将绘制一条线段,从点 (10,20) 到点 (100,200)。你可以将几个连接的线段组合成一个 L 命令。例如,上面给出的 <polygon> 示例可以使用 <path> 元素创建:

<path d="M 0,0 L 100,0 100,75 50,100 0,75 Z"/>

数据末尾的 Z 关闭了路径,通过添加最后一条边,形成多边形。(请注意,和往常一样,你可以在数据中使用逗号或空格。)

C 命令使用六个数字作为数据,来指定三次贝塞尔曲线段的两个控制点和最终端点。你也可以给出多个六个值来获得一系列连接的曲线段。类似地,Q 命令使用四个数据值来指定二次贝塞尔曲线段的控制点和最终端点。前面在本节图片中展示的大、弯曲、黄色形状是通过两个线段和两个贝塞尔曲线段创建的路径:

<path 
d="M 20,70 C 150,70 250,350 380,350 L 380,380 C 250,380 150,100 20,100 Z"
fill="yellow" stroke-width="2" stroke="black"/>

SVG 路径通过定义路径命令的 "相对" 版本来增加了灵活性,其中命令的数据相对于当前位置给出。例如,相对移动命令不是告诉要移动到哪里,而是告诉从当前位置移动多远。路径命令的相对版本的名称是小写字母,而不是大写字母。"M 10,20" 意味着移动到坐标 (10,20) 的点,而 "m 10,20" 意味着从当前位置水平移动 10 个单位,垂直移动 20 个单位。类似地,如果当前位置是 (x,y),则命令 "l 3,5"(第一个字符是小写的 L)会从 (x,y) 绘制一条线到 (x+3,y+5)。

SVG has some nice features for making more complex shapes. The <polygon> element makes it easy to create a polygon from a list of coordinate pairs. For example,

<polygon points="0,0 100,0 100,75 50,100 0,75"/>

creates a five-sided polygon with vertices at (0,0), (100,0), (100,75), (50,100), and (0,75). Every pair of numbers in the points attribute specifies a vertex. The numbers can be separated by either spaces or commas. I've used a mixture of spaces and commas here to make it clear how the numbers pair up. Of course, you can add the usual style attributes for stroke and fill to the polygon element. A <polyline> is similar to a <polygon>, except that it leaves out the last line from the final vertex back to the starting vertex. The difference only shows up when a polyline is stroked; a polyline is filled as if the missing side were added.

The <path> element is much more interesting. In fact, all of the other basic shapes, except text, could be made using path elements. A path can consist of line segments, Bezier curves, and elliptical arcs (although I won't discuss elliptical arcs here). The syntax for specifying a path is very succinct, and it has some features that we have not seen before. A path element has an attribute named d that contains the data for the path. The data consists of one or more commands, where each command consists of a single letter followed by any data necessary for the command. The moveTo, lineTo, cubic Bezier, and quadratic Bezier commands that you are already familiar with are coded by the letters M, L, C, and Q. The command for closing a path segment is Z, and it requires no data. For example the path data "M 10 20 L 100 200" would draw a line segment from the point (10,20) to the point (100,200). You can combine several connected line segments into one L command. For example, the <polygon> example given above could be created using the <path> element

<path d="M 0,0 L 100,0 100,75 50,100 0,75 Z"/>

The Z at the end of the data closes the path by adding the final side to the polygon. (Note that, as usual, you can use either commas or spaces in the data.)

The C command takes six numbers as data, to specify the two control points and the final endpoint of the cubic Bezier curve segment. You can also give a multiple of six values to get a connected sequence of curve segements. Similarly, the Q command uses four data values to specify the control point and final endpoint of the quadratic Bezier curve segment. The large, curvy, yellow shape shown in the picture earlier in this section was created as a path with two line segments and two Bezier curve segments:

<path 
d="M 20,70 C 150,70 250,350 380,350 L 380,380 C 250,380 150,100 20,100 Z"
fill="yellow" stroke-width="2" stroke="black"/>

SVG paths add flexibility by defining "relative" versions of the path commands, where the data for the command is given relative to the current position. A relative move command, for example, instead of telling where to move, tells how far to move from the current position. The names of the relative versions of the path commands are lower case letters instead of upper case. "M 10,20" means to move to the point with coordinates (10,20), while "m 10,20" means to move 10 units horizontally and 20 units vertically from the current position. Similarly, if the current position is (x,y), then the command "l 3,5", where the first character is a lower case L, draws a line from (x,y) to (x+3,y+5).

2.7.4 Hierarchical Models

Hierarchical Models

SVG 如果只能处理单个简单形状,那将不是一个很有趣的语言。对于复杂的场景,我们希望能够进行分层建模,其中对象可以由子对象构建,并且可以将变换应用于整个复杂对象。我们需要一种方法来对对象进行分组,以便它们可以作为一个单元进行处理。为此,SVG 使用了 <g> 元素。<g> 元素的内容是一系列形状元素,可以是简单形状或嵌套的 <g> 元素。

你可以为 <g> 元素添加样式和变换属性。分组的主要作用是将组作为单个对象处理。在 <g> 中的变换属性将整个组作为一个整体进行变换。在 <g> 元素上的样式属性,如 fill 或 font-family,将为该组设置默认值,替换当前的默认值。以下是一个示例:

<g fill="none" stroke="black" stroke-width="2" transform="scale(1,-1)">
    <circle r="98"/>
    <ellipse cx="40" cy="40" rx="20" ry="7"/>
    <ellipse cx="-40" cy="40" rx="20" ry="7"/>
    <line y1="20" y2="-10"/>
    <path d="M -40,-40 C -30,-50 30,-50 40,-40" stroke-width="4"/>
</g>

嵌套的形状使用 fill="none" stroke="black" stroke-width="2" 作为属性的默认值。可以通过为元素指定不同的值来覆盖默认值,就像在此示例中为 <path> 元素的 stroke-width 属性所做的那样。在组中设置 transform="scale(1,−1)" 将整个图像垂直翻转。我之所以这样做,仅因为我更喜欢使用一个坐标系统,其中 y 从下到上增加,而不是从上到下增加。以下是此组生成的简单线描绘的脸部图案:

pixel-coordinates

现在,假设我们想在场景中包含多个对象的多个副本。不应该需要重复绘制对象的代码。像可重用子例程一样会很好。事实上,SVG 有非常相似的东西:你可以在 <defs> 元素内定义可重用对象。在 <defs> 中定义的对象不会添加到场景中,但可以使用单个命令将对象的副本添加到场景中。为了使其工作,对象必须具有用于标识它的 id 属性。例如,我们可以定义一个看起来像加号的对象:

<defs>
<g id="plus" stroke="black">
    <line x1="-20" y1="0" x2="20" y2="0"/>
    <line x1="0" y1="-20" x2="0" y2="20"/>
</g>
</defs>

然后,可以使用 <use> 元素将加号对象的副本添加到场景中。语法是:

<use xlink:href="#plus"/>

xlink:href 属性的值必须是对象的 id,前面加上一个 "#" 字符。(不要忘记 #。如果你漏掉它,<use> 元素将被简单地忽略。)你可以为 <use> 元素添加 transform 属性,以将变换应用于对象的副本。你还可以应用样式属性,这些属性将用作副本中属性的默认值。例如,我们可以使用不同的变换和描边宽度绘制几个加号:

<use xlink:href="#plus" transform="translate(50,20)" stroke-width="5"/>
<use xlink:href="#plus" transform="translate(0,30) rotate(45)"/>

请注意,我们无法更改加号的颜色,因为它已经指定了自己的描边颜色。

<defs> 部分定义的对象也可以用作其他对象定义中的子对象。这使得可以创建具有多个级别的层次结构。以下是从 svg/svg-hierarchy.svg 中的示例中定义了一个 "轮子" 对象,然后在 "车" 对象中将两个轮子的副本用作子对象的示例:

<defs>

<!-- Define an object that represents a wheel centered at (0,0) and with
    radius 1.  The wheel is made out of several filled circles, with
    thin rectangles for the spokes. -->

<g id="wheel">
    <circle cx="0" cy="0" r="1" fill="black"/>
    <circle cx="0" cy="0" r="0.8" fill="lightGray"/>
    <rect x="-0.9" y="-0.05" width="1.8" height=".1" fill="black"/>
    <rect x="-0.9" y="-0.05" width="1.8" height=".1" fill="black" 
                                            transform="rotate(120)"/>
    <rect x="-0.9" y="-0.05" width="1.8" height=".1" fill="black" 
                                            transform="rotate(240)"/>
    <circle cx="0" cy="0" r="0.2" fill="black"/>
</g>

<!-- Define an object that represents a cart made out of two wheels,
    with two rectangles for the body of the cart. -->

<g id="cart">
<use xlink:href="#wheel" transform="translate(-1.5,-0.1) scale(0.8,0.8)"/>
<use xlink:href="#wheel" transform="translate(1.5,-0.1) scale(0.8,0.8)"/>
<rect x="-3" y="0" width="6" height="2"/>
<rect x="-2.3" y="1.9" width="2.6" height="1"/>
</g>

</defs>

SVG 文件继续

向图像添加了一个轮子的副本和四个车的副本。这四个车具有不同的颜色和变换。以下是图像:

pixel-coordinates

SVG would not be a very interesting language if it could only work with individual simple shapes. For complex scenes, we want to be able to do hierarchical modeling, where objects can be constructed from sub-objects, and a transform can be applied to an entire complex object. We need a way to group objects so that they can be treated as a unit. For that, SVG has the <g> element. The content of a <g> element is a list of shape elements, which can be simple shapes or nested <g> elements.

You can add style and transform attributes to a <g> element. The main point of grouping is that a group can be treated as a single object. A transform attribute in a <g> will transform the entire group as a whole. A style attribute, such as fill or font-family, on a <g> element will set a default value for the group, replacing the current default. Here is an example:

<g fill="none" stroke="black" stroke-width="2" transform="scale(1,-1)">
    <circle r="98"/>
    <ellipse cx="40" cy="40" rx="20" ry="7"/>
    <ellipse cx="-40" cy="40" rx="20" ry="7"/>
    <line y1="20" y2="-10"/>
    <path d="M -40,-40 C -30,-50 30,-50 40,-40" stroke-width="4"/>
</g>

The nested shapes use fill="none" stroke="black" stroke-width="2" for the default values of the attributes. The default can be overridden by specifying a different value for the element, as is done for the stroke-width of the <path> element in this example. Setting transform="scale(1,−1)" for the group flips the entire image vertically. I do this only because I am more comfortable working in a coordinate system in which y increases from bottom-to-top rather than top-to-bottom. Here is the simple line drawing of a face that is produced by this group:

<figure markdown="span">
    ![pixel-coordinates](../../en/c2/svg-face.svg)
</figure>

Now, suppose that we want to include multiple copies of an object in a scene. It shouldn't be necessary to repeat the code for drawing the object. It would be nice to have something like reusable subroutines. In fact, SVG has something very similar: You can define reusable objects inside a `<defs>` element. An object that is defined inside `<defs>` is not added to the scene, but copies of the object can be added to the scene with a single command. For this to work, the object must have an id attribute to identify it. For example, we could define an object that looks like a plus sign:

```xml
<defs>
<g id="plus" stroke="black">
    <line x1="-20" y1="0" x2="20" y2="0"/>
    <line x1="0" y1="-20" x2="0" y2="20"/>
</g>
</defs>

A <use> element can then be used to add a copy of the plus sign object to the scene. The syntax is

<use xlink:href="#plus"/>

The value of the xlink:href attribute must be the id of the object, with a "#" character added at the beginning. (Don't forget the #. If you leave it out, the <use> element will simply be ignored.) You can add a transform attribute to the <use> element to apply a transformation to the copy of the object. You can also apply style attributes, which will be used as default values for the attributes in the copy. For example, we can draw several plus signs with different transforms and stroke widths:

<use xlink:href="#plus" transform="translate(50,20)" stroke-width="5"/>
<use xlink:href="#plus" transform="translate(0,30) rotate(45)"/>

Note that we can't change the color of the plus sign, since it already specifies its own stroke color.

An object that has been defined in the <defs> section can also be used as a sub-object in other object definitions. This makes it possible to create a hierarchy with multiple levels. Here is an example from svg/svg-hierarchy.svg that defines a "wheel" object, then uses two copies of the wheel as sub-objects in a "cart" object:

<defs>

<!-- Define an object that represents a wheel centered at (0,0) and with
    radius 1.  The wheel is made out of several filled circles, with
    thin rectangles for the spokes. -->

<g id="wheel">
    <circle cx="0" cy="0" r="1" fill="black"/>
    <circle cx="0" cy="0" r="0.8" fill="lightGray"/>
    <rect x="-0.9" y="-0.05" width="1.8" height=".1" fill="black"/>
    <rect x="-0.9" y="-0.05" width="1.8" height=".1" fill="black" 
                                            transform="rotate(120)"/>
    <rect x="-0.9" y="-0.05" width="1.8" height=".1" fill="black" 
                                            transform="rotate(240)"/>
    <circle cx="0" cy="0" r="0.2" fill="black"/>
</g>

<!-- Define an object that represents a cart made out of two wheels,
    with two rectangles for the body of the cart. -->

<g id="cart">
<use xlink:href="#wheel" transform="translate(-1.5,-0.1) scale(0.8,0.8)"/>
<use xlink:href="#wheel" transform="translate(1.5,-0.1) scale(0.8,0.8)"/>
<rect x="-3" y="0" width="6" height="2"/>
<rect x="-2.3" y="1.9" width="2.6" height="1"/>
</g>

</defs>

The SVG file goes on to add one copy of the wheel and four copies of the cart to the image. The four carts have different colors and transforms. Here is the image:

pixel-coordinates

2.7.5 Animation

Animation

SVG 有许多高级功能,我这里不会讨论,但我想提一下一个:动画。几乎可以对 SVG 对象的任何属性进行动画,包括几何形状、样式和变换。动画的语法本身相当复杂,我只会做一些例子。但我会告诉你足够的内容来产生一个相当复杂的层次动画,就像在 Subsection 2.4.1 中讨论和用作演示的 "cart-and-windmills" 示例。该动画的 SVG 版本可以在 svg/cart-and-windmills.svg 中找到。这是它的样子,尽管某些网络浏览器可能不会显示动画,因为有些浏览器没有正确或完全实现 SVG 动画

pixel-coordinates

形状元素的许多属性都可以通过在形状元素的内容中添加 元素来进行动画处理。以下是一个示例,使一个矩形从左向右移动图像:

<rect x="0" y="210" width="40" height="40">
    <animate attributeName="x"
    from="0" to="430" dur="7s"
    repeatCount="indefinite"/>
</rect>

请注意,<animate> 嵌套在 <rect> 内部。attributeName 属性告诉我们正在对 <rect> 的哪个属性进行动画处理,这里是 x。fromto 属性表示 x 将取值从 0 到 430。dur 属性是 "持续时间",即动画持续的时间;值 "7s" 表示 "7 秒"。repeatCount="indefinite" 属性表示动画完成后,它将重新开始,并且将无限重复,也就是说,只要图像显示出来就会一直重复。如果省略了 repeatCount 属性,则动画运行一次后,矩形将跳回到其原始位置并保持在那里。如果将 repeatCount 替换为 fill="freeze",则在动画运行后,矩形将被冻结在其最终位置,而不是跳回到起始位置。动画在图像首次加载时开始。如果希望动画在稍后的时间开始,可以添加一个 begin 属性,其值给出动画应该在图像加载后的多少秒后开始。

如果我们希望矩形在初始和最终位置之间来回移动怎么办?为此,我们需要一种称为关键帧动画的东西,这是一个重要的概念。fromto 属性只允许你在动画的开始和结束时指定值。在关键帧动画中,在动画中间的其他时间点指定值。对于 SVG 中的关键帧动画,fromto 属性被 keyTimes 和 values 替换。以下是我们的移动矩形示例,修改为使用关键帧:

<rect x="0" y="210" width="40" height="40">
    <animate attributeName="x"
    keyTimes="0;0.5;1" values="0;430;0" dur="7s"
    repeatCount="indefinite"/>
</rect>

keyTimes 属性是一个由分号分隔的数字列表。这些数字在 0 到 1 的范围内,并且应按升序排列。第一个数字应为 0,最后一个数字应为 1。一个数字表示动画期间的时间,作为完整动画的一部分的分数。例如,0.5 是动画进行到一半的时间点,0.75 是动画进行到四分之三的时间点。values 属性是一个值的列表,每个关键时间点有一个值。在这种情况下,x 的值在动画开始时为 0,在动画进行到一半时为 430,然后在动画结束时再次为 0。在关键时间点之间,通过对关键时间点指定的值进行插值来获取 x 的值。在这种情况下的结果是,在动画的前半部分,矩形从左向右移动,然后在后半部分从右向左移动。

变换也可以被动画化,但是你需要使用 <animateTransform> 标签而不是 <animate>,并且你需要添加一个 type 属性来指定你正在动画化的变换,比如 "rotate" 或 "translate"。例如,下面是应用于一个组的变换动画:

<g transform="scale(0,0)">
    <animateTransform attributeName="transform" type="scale"
        from="0,0" to="0.4,0.7"
        begin="3s" dur="15s" fill="freeze"/>
    <rect x="-15" y="0" width="30" height="40" fill="rgb(150,100,0)"/>
    <polygon points="-60,40 60,40 0,200" fill="green"/>
</g>

该动画显示了一个由绿色三角形和棕色矩形组成的不断增长的 "树"。在动画中,变换从 scale(0,0) 到 scale(0.4,0.7)。动画在图像加载后的 3 秒后开始,持续 15 秒。在动画结束时,树以最终缩放比冻结。<g> 元素上的 transform 属性指定了直到动画开始时生效的缩放。(缩放因子为 0 会将对象折叠为大小为零,使其不可见。)你可以在示例文件 svg/first-svg-animation.svg 中找到这个示例,以及一个移动的矩形和一个关键帧动画。以下是动画本身。要查看不断增长的树,你可能需要重新加载此页面或在单独的窗口中查看图像:

pixel-coordinates

你可以在 SVG 文件的 <defs> 部分创建动画对象,并且可以将动画应用于 <use> 元素。这使得创建分层动画成为可能。这里有一个简单的例子:

pixel-coordinates

这个示例显示了一个旋转的六边形,每个六边形顶点都有一个旋转的正方形。六边形是由一个对象的六个副本构成的,每个副本应用了不同的旋转。(六边形右侧的图像显示了基本对象的一个副本。)正方形被定义为一个带有自己旋转的动画对象。它被用作六边形中的一个子对象。施加在六边形上的旋转也适用于正方形,覆盖了它自己内置的旋转。这就是这个示例是分层动画的原因。

现在回顾一下cart-and-windmills的示例,你可能已经能够看出如何做动画了。别忘了查看源代码,它非常简短!

SVG has a number of advanced features that I won't discuss here, but I do want to mention one: animation. It is possible to animate almost any property of an SVG object, including geometry, style, and transforms. The syntax for animation is itself fairly complex, and I will only do a few examples. But I will tell you enough to produce a fairly complex hierarchical animation like the "cart-and-windmills" example that was discussed and used as a demo in Subsection 2.4.1. An SVG version of that animation can be found in svg/cart-and-windmills.svg. Here is what it looks like, although some web browsers might not show the animation, since some browsers do not implement SVG animations correctly or at all:

pixel-coordinates

Many attributes of a shape element can be animated by adding an element to the content of the shape element. Here is an example that makes a rectangle move across the image from left to right:

<rect x="0" y="210" wid th="40" height="40">
    <animate attributeName="x"
    from="0" to="430" dur="7s"
    repeatCount="indefinite"/>
</rect>

Note that the <animate> is nested inside the <rect>. The attributeName attribute tells which attribute of the <rect> is being animated, in this case, x. The from and to attributes say that x will take on values from 0 to 430. The dur attribute is the "duration", that is, how long the animation lasts; the value "7s" means "7 seconds." The attribute repeatCount="indefinite" means that after the animation completes, it will start over, and it will repeat indefinitely, that is, as long as the image is displayed. If the repeatCount attribute is omitted, then after the animation runs once, the rectangle will jump back to its original position and remain there. If repeatCount is replaced by fill="freeze", then after the animation runs, the rectangle will be frozen in its final position, instead of jumping back to the starting position. The animation begins when the image first loads. If you want the animation to start at a later time, you can add a begin attribute whose value gives the time when the animation should start, as a number of seconds after the image loads.

What if we want the rectangle to move back and forth between its initial and final position? For that, we need something called keyframe animation, which is an important idea in its own right. The from and to attributes allow you to specify values only for the beginning and end of the animation. In a keyframe animation, values are specified at additional times in the middle of the animation. For a keyframe animation in SVG, the from and to attributes are replaced by keyTimes and values. Here is our moving rectangle example, modified to use keyframes:

<rect x="0" y="210" width="40" height="40">
    <animate attributeName="x"
    keyTimes="0;0.5;1" values="0;430;0" dur="7s"
    repeatCount="indefinite"/>
</rect>

The keyTimes attribute is a list of numbers, separated by semicolons. The numbers are in the range 0 to 1, and should be in increasing order. The first number should be 0 and the last number should be 1. A number specifies a time during the animation, as a fraction of the complete animation. For example, 0.5 is a point half-way through the animation, and 0.75 is three-quarters of the way. The values attribute is a list of values, with one value for each key time. In this case, the value for x is 0 at the start of the animation, 430 half-way through the animation, and 0 again at the end of the animation. Between the key times, the value for x is obtained by interpolating between the values specified for the key times. The result in this case is that the rectangle moves from left to right during the first half of the animation and then back from right to left in the second half.

Transforms can also be animated, but you need to use the <animateTransform> tag instead of <animate>, and you need to add a type attribute to specify which transform you are animating, such as "rotate" or "translate". Here, for example, is a transform animation applied to a group:

<g transform="scale(0,0)">
    <animateTransform attributeName="transform" type="scale"
        from="0,0" to="0.4,0.7"
        begin="3s" dur="15s" fill="freeze"/>
    <rect x="-15" y="0" width="30" height="40" fill="rgb(150,100,0)"/>
    <polygon points="-60,40 60,40 0,200" fill="green"/>
</g>

The animation shows a growing "tree" made from a green triangle and a brown rectangle. In the animation, the transform goes from scale(0,0) to scale(0.4,0.7). The animation starts 3 seconds after the image loads and lasts 15 seconds. At the end of the animation, the tree freezes at its final scale. The transform attribute on the <g> element specifies the scale that is in effect until the animation starts. (A scale factor of 0 collapses the object to size zero, so that it is invisible.) You can find this example, along with a moving rectangle and a keyframe animation, in the sample file svg/first-svg-animation.svg. Here is the animation itself. To see the growing trees, you might have to reload this page or view the image in a separate window:

pixel-coordinates

You can create animated objects in the <defs> section of an SVG file, and you can apply animation to <use> elements. This makes it possible to create hierarchical animations. Here is a simple example:

pixel-coordinates

The example shows a rotating hexagon with a rotating square at each vertex of the hexagon. The hexagon is constructed from six copies of one object, with a different rotation applied to each copy. (A copy of the basic object is shown in the image to the right of the hexagon.) The square is defined as an animated object with its own rotation. It is used as a sub-object in the hexagon. The rotation that is applied to the hexagon applies to the square, on top of its own built-in rotation. That's what makes this an example of hierarchical animation.

If you look back at the cart-and-windmills example now, you can probably see how to do the animation. Don't forget to check out the source code, which is surprisingly short!