HTML5的 2D SVG和SVG DOM的学习笔记(1)

时间:2023-12-25 08:31:01

(项目中要使用SVG,只好补充知识了)

HTML体系中,最常用的绘制矢量图的技术是SVG和HTML5新增加的canvas元素。这两种技术都支持绘制矢量图和光栅图。

HTML5的 2D SVG和SVG DOM的学习笔记(1)

一、SVG概述

  可缩放矢量图形(Scalable Vector Graphics,简称SVG)是一种使用XML来描述二维图形的语言(SVG严格遵从XML语法)。 SVG允许三种类型的图形对象:矢量图形形状(例如由直线和曲线组成的路径)、图像和文本。 可以将图形对象(包括文本)分组、样式化、转换和组合到以前呈现的对象中。 SVG 功能集包括嵌套转换、剪切路径、alpha 蒙板和模板对象。

  SVG绘图是交互式和动态的。 例如,可使用脚本来定义和触发动画。这一点与Flash相比很强大。Flash是二进制文件,动态创建和修改都比较困难。而SVG是文本文件,动态操作是相当容易的。而且,SVG直接提供了完成动画的相关元素,操作起来非常方便。

  SVG与其他Web标准兼容,并直接支持文档对象模型DOM。这一点也是与HTML5中的canvas相比很强大的地方(这里注意,SVG内部 也是用一个类似的canvas这样的东西来展示SVG图形,到后面你会发现很多特性和HTML5的canvas还有点像;文中如果没明确说明是SVG的 canvas的话,都代指HTML5中的canvas元素)。因而,可以很方便的使用脚本实现SVG的很多高级应用。而且SVG的图形元素基本上都支持 DOM中的标准事件。可将大量事件处理程序(如“onmouseover”和“onclick”)分配给任何SVG图形对象。 虽然SVG的渲染速度比不上canvas元素,但是胜在DOM操作很灵活,这个优势完全可以弥补速度上的劣势。

  SVG既可以说是一种协议,也可以说是一门语言;既是HTML的一个标准元素,也是一种图片格式SVG并不是HTML5中的东西,但是也算页面时兴的技术之一

SVG与其它图片格式的比较

  SVG与其它的图片格式相比,有很多优点(很多优点来源于矢量图的优点):

• SVG文件是纯粹的XML, 可被非常多的工具读取和修改(比如记事本)。
• SVG 与JPEG 和GIF图像比起来,尺寸更小,且可压缩性更强。
• SVG 是可伸缩的,可在图像质量不下降的情况下被放大,可在任何的分辨率下被高质量地打印。
• SVG 图像中的文本是可选的,同时也是可搜索的(很适合制作地图)。
• SVG 可以与 Java 技术一起运行。
• SVG 是开放的标准。

SVG与Flash的比较 

  SVG 的主要竞争者是Flash。与Flash相比,SVG 最大的优势是它与其他标准(比如XSL和DOM)相兼容,操作方便,而Flash则是未开源的私有技术。其它的比如存储的格式,动态生成图形等方面,SVG也占有很大的优势。

二、SVG在网页中的呈现方式

2. 1.独立的SVG文件/页面,定义的模板基本就像下面的一样:

<?xml version="1.0"?>
<!DOCTYPE svg PUBLIC "//W3C//DTD SVG 1.0//EN" "http://www.w3.org/TR/2001/REC-SVG-20010904/DTD/svg10.dtd">
<svg width="100%" height="100%">
<!-- SVG markup here. -->
</svg>

把这样的文本文件保存成以svg为扩展名的文件,例如sun.svg,这样的文件可以直接用浏览器打开浏览,也可以作为引用嵌入到别的页面中。

2.2. 内联到HTML--包含在<svg>标签内部

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE html>
<html>
<head>
<!-- <meta content="text/html; charset=utf-8" http-equiv="Content-Type" /> -->
<title> My First SVG Page</title>
</head>
<body>
<svg xmlns="http://www.w3.org/2000/svg" version="1.1"
width="200px" height="200px">
<rect x="0" y="0" width="100%" height="100%"
fill="none" stroke="black"/>
<circle cx="100" cy="100" r="50"
style="stroke: black; fill: red;"/>
</svg>
</body>
</html>

2.3.HTML引用外部的SVG文件
  使用object或者img元素嵌入svg图形就可以了,例如下面的小例子:

<!DOCTYPE html>
<html>
<head>
  <title> My First SVG Page</title>
</head>
<body>
  <object data="sun.svg" type="image/svg+xml" width="300px" height="300px">
    <!-- Implement fallback code here, or display a message: -->
    <p>Your browser does not support SVG - please upgrade to a modern browser.</p>
  </object>
  <img src="sun.svg" alt="svg not supported!" />
</body>
</html>

有的浏览器必须使用embed标签才能显示SVG

<embed name="SVG_PowerSystemGraph" width="780px" height="350px" src="../SVGFile/PowerSystemGraph.svg" type="image/svg+xml"></embed>

其实SVG也可以放在其他的XML文档中,也可以像其他的XML文档一样,使用XML相关的技术格式化和验证,这个不是重点,此处略去了。

2.4.通过CSS:background: url(...)

<style type="text/css">
#cat{
width: 140px;height: 170px;
background: url(DEMO1.svg) no-repeat;
}
</style> <div id="cat"></div>

SVG的渲染顺序

  SVG是严格按照定义元素的顺序来渲染的,这个与HTML靠z-index值来控制分层不一样。在SVG中,写在前面的元素先被渲染,写在后面 的元素后被渲染。后渲染的元素会覆盖前面的元素,虽然有时候受透明度影响,看起来不是被覆盖的,但是SVG确实是严格按照先后顺序来渲染的。

三、SVG的图形绘制,蒙版、滤镜——可参考SVG文档

3.1 基本 SVG 形状

SVG 定义了六种基本形状,这些基本形状和路径(在路径是什么?中讨论)一道,可以组合起来形成任何可能的图像。每个基本形状都带有指定其位置和大小的属性。它们的颜色和轮廓分别由 fill 和 stroke 属性确定。这些形状是:

  • 圆(circle):显示一个圆心在指定点、半径为指定长度的标准的圆。
  • 椭圆(ellipse):显示中心在指定点、长轴和短轴半径为指定长度的椭圆。
  • 矩形(rect):显示左上角在指定点并且高度和宽度为指定值的矩形(包括正方形)。也可以通过指定边角圆的 x和 y 半径画成圆角矩形。
  • 线(line):显示两个坐标之间的连线。
  • 折线(polyline):显示顶点在指定点的一组线。
  • 多边形(polygon):类似于 polyline,但增加了从最末点到第一点的连线,从而创建了一个闭合形状。

3.2 定义可重用部件

通常在 SVG 图像的构建中,各部分或者是可重用的,或者不便于在图像主体内定义。在这些情况下,通常方便的做法是在文档的定义部分内(作为 <defs></defs> 元素的一部分)通过给这些部分指定以后可在图像主体中调用的标识来创建它们。

例如,显示的图像有两只眼睛,每只眼睛边缘有一个眼镜镜片。这个文档可以在定义部分中定义一个镜片,然后在文档中调用它两次,而不是创建这个镜片两次(如所示)。类似地,眼睛本身可以包含渐变(gradient),也应该定义这个渐变供以后引用。

请注意可重用元素也允许每次使用不同的属性值,正如上面虹膜的例子中的填充属性所示

3.3 笔划与填充

  • fill:该属性指定用来填充对象内部区域的颜料。大多数情况下,该属性只是一种颜色,但它也可以是渐变或图案(会在图案中介绍)。这个值通常是关键字、颜色说明或指向预定义元素的 URI。
  • fill-opacity:该属性指定元素的透明性。值的范围从完全透明(0)到完全不透明(1)。
  • stroke:该属性指定元素外边框的外观。象 fill 一样,它引用颜料,尽管通常将它指定为一种简单颜色。
  • stroke-width:该属性指定笔划线的宽度。
  • stroke-linecap:该属性确定线末端的形状,可取的值有粗端(缺省值)、圆和正方形。
  • stroke-linejoin:该属性确定对象各角的外观。允许的值有直角(缺省值)、圆和斜角,它如示例中所示将尖角的边缘“剪掉”。
  • stroke-dasharray:该属性是一个整数系列(如 3、2、3、2、4、3、2 和 3),它允许对虚线中每一划的相对长度进行控制。
  • stroke-opacity:类似于 fill-opacity,该属性确定元素笔划线的相对透明性。
    <?xml version="1.0" standalone="no" ?>
    <!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.0//EN"
    "http://www.w3.org/TR/2001/REC-SVG-20010904/DTD/svg10.dtd"> <svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink"
    width="300" height="300"> <desc>Stroke and fill</desc>
    <defs>
    <linearGradient id="lineGradient">
    <stop offset="0%" stop-color="red" />
    <stop offset="100%" stop-color="yellow" />
    </linearGradient>
    <polygon id="lens" points="65,50 185,50 185,75, 150,100 100,100 65,75"
    fill="pink" stroke="purple" stroke-width="4"
    fill-opacity=".5"/>
    </defs>
    <g>
    <line x1="65" y1="50" x2="310" y2="50" stroke="plum" stroke-width="2"/>
    <!-- Box with gradient along the outside -->
    <rect x="50" y="125" width="275" height="40" fill="orange"
    stroke-width="6" stroke="url(#lineGradient)" />
    <!-- Purple line with rounded edges -->
    <line x1="65" y1="190" x2="310" y2="190"
    stroke="purple" stroke-width="20"
    stroke-linecap="round"/>
    <!-- Blue polygon with beveled corners -->
    <polygon points="50,250 100,225 300,225 200,275" stroke="blue"
    fill="none" stroke-width="10" stroke-linejoin="bevel" />
    </g>
    </svg>

3.4 渐变

使用 linearGradient和radialGradient 属性向量进行变换

  • 线性渐变,假设它从要填充区域的左缘开始到右缘结束。可以用x1、y1、x2 和 y2 属性更改这一向量
  • 放射性渐变,渐变基于一个圆,可以用 cx、cy 和 r 属性调整外部圆(渐变向量终止的地方)的圆心和半径。可以使用 fx 和 fy 属性调整焦点(渐变向量起始的地方)。
    <?xml version="1.0"?>
    
    <!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.0//EN"
    "http://www.w3.org/TR/2001/REC-SVG-20010904/DTD/svg10.dtd">
    <svg width="400" height="400" xmlns="http://www.w3.org/2000/svg"> <desc>Colors</desc>
    <defs>
    <linearGradient id="linear1">
    <stop offset="0%" stop-color="red"/>
    <stop offset="50%" stop-color="white"/>
    <stop offset="100%" stop-color="blue"/>
    </linearGradient>
    <linearGradient id="linear2" x1="100%" y1="0%" x2="0%" y2="100%">
    <stop offset="0%" stop-color="red"/>
    <stop offset="50%" stop-color="white"/>
    <stop offset="100%" stop-color="blue"/>
    </linearGradient>
    <linearGradient id="linear3" gradientTransform="rotate(90)">
    <stop offset="0%" stop-color="red"/>
    <stop offset="50%" stop-color="white"/>
    <stop offset="100%" stop-color="blue"/>
    </linearGradient>
    <radialGradient id="radial1">
    <stop offset="0%" stop-color="red"/>
    <stop offset="50%" stop-color="white"/>
    <stop offset="100%" stop-color="blue"/>
    </radialGradient>
    <radialGradient id="radial2" fx="225" fy="225">
    <stop offset="0%" stop-color="red"/>
    <stop offset="50%" stop-color="white"/>
    <stop offset="100%" stop-color="blue"/>
    </radialGradient>
    <radialGradient id="radial3" cx="25%" cy="25%" r="75%">
    <stop offset="0%" stop-color="red"/>
    <stop offset="50%" stop-color="white"/>
    <stop offset="100%" stop-color="blue"/>
    </radialGradient>
    </defs> <g>
    <!-- First row -->
    <rect x="10" y="10" height="100" width="100" stroke="black"
    fill="url(#linear1)"/>
    <rect x="125" y="10" height="100" width="100" stroke="black"
    fill="url(#linear2)"/>
    <rect x="240" y="10" height="100" width="100" stroke="black"
    fill="url(#linear3)"/>
    <!-- Second row -->
    <rect x="10" y="125" height="100" width="100" stroke="black"
    fill="url(#radial1)"/>
    <rect x="125" y="125" height="100" width="100" stroke="black"
    fill="url(#radial2)"/>
    <rect x="240" y="125" height="100" width="100" stroke="black"
    fill="url(#radial3)"/>
    </g>
    </svg>

3.5 图案

定义图案与定义任何其它作为 SVG 图像一部分出现的对象相似。它有位置、高度和宽度,通常还有一个或多个包含的对象。

位置是基于整个文档还是基于正在被填充的对象由 patternUnits 属性确定,该属性可以设置为objectBoundingBox 或 userSpaceOnUse;这些属性分别设置基于对象和文档的坐标。与渐变相似,可以变换图案(使用 patternTransform 属性)

<?xml version="1.0"?>

<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.0//EN"
"http://www.w3.org/TR/2001/REC-SVG-20010904/DTD/svg10.dtd">
<svg width="400" height="200" xmlns="http://www.w3.org/2000/svg">
<desc>Patterns</desc>
<defs>
<pattern id="notes" x="0" y="0" width="50" height="75"
patternTransform="rotate(15)"
patternUnits="userSpaceOnUse">
<ellipse cx="10" cy="30" rx="10" ry="5"/>
<line x1="20" y1="30" x2="20" y2="0"
stroke-width="3" stroke="black"/>
<line x1="20" y1="0" x2="30" y2="5"
stroke-width="3" stroke="black"/>
</pattern>
</defs> <g>
<ellipse cx="175" cy="100" rx="125" ry="60"
fill="url(#notes)" stroke="black" stroke-width="5"/>
</g>
</svg>

3.6 滤镜

对 SVG 图像的滤镜操作包括创建一系列滤镜原语操作,该系列中每个原语操作都有自己的目的。例如,偏移滤镜按指定信息将源图像左移或右移以及上移或下移。高斯模糊原语操作按要求对源图像进行模糊处理。

源图像不必一定是实际的 SVG 图像。例如,它可以是前一个原语操作的结果。下面的代码将几个滤镜应用到前一页中显示的图案。

<?xml version="1.0"?>
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.0//EN"
"http://www.w3.org/TR/2001/REC-SVG-20010904/DTD/svg10.dtd">
<svg width="400" height="200" xmlns="http://www.w3.org/2000/svg"
xmlns:xlink="http://www.w3.org/1999/xlink">
<desc>Filters</desc>
<defs>
<filter id="dropShadow" filterUnits="userSpaceOnUse"
x="0" y="0" width="400" height="200">
<feOffset in="SourceAlpha" dx="5" dy="5" result="offset"/>
<feGaussianBlur in="offset" stdDeviation="5" result="blur"/>
<feMerge>
<feMergeNode in="blur"/>
<feMergeNode in="SourceGraphic"/>
</feMerge>
</filter>
<pattern id="notes" x="0" y="0" width="50" height="75"
patternTransform="rotate(15)"
patternUnits="userSpaceOnUse">
<ellipse cx="10" cy="30" rx="10" ry="5"/>
<line x1="20" y1="30" x2="20" y2="0"
stroke-width="3" stroke="black"/>
<line x1="20" y1="0" x2="30" y2="5"
stroke-width="3" stroke="black"/>
</pattern>
</defs> <g>
<ellipse filter="url(#dropShadow)" cx="175" cy="100"
rx="125" ry="60"
fill="url(#notes)" stroke="black" stroke-width="5"/>
</g>
</svg>
  • 首先,偏移滤镜将原始椭圆及其图案的 alpha 通道作为源(使用 in 属性)。alpha 通道由与图像中每个非白色像素对应的黑色像素组成,并且用 SourceAlpha 关键字指定。偏移原语操作完成处理然后将结果输出到一个由 result 属性指定的缓冲区(本例中的缓冲区名为 offset )。
  • 接下来,模糊原语操作接任。它将 in 参数指定的 offset 缓冲区的内容作为源。然后,它将其结果输出到 result 属性指定的名为 blur 的缓冲区。
  • 这时,滤镜仅由经过偏移和模糊的图像组成。如果滤镜操作到此为止,那么页面上只出现模糊处理的图像。合并原语操作取得 blur 缓冲区的内容然后将它与原始源图形合并,正如当 in 属性引用 SourceGraphic 关键字时所指定的那样。

所有处理的结果是一幅具有阴影效果的原始图像

3.7 坐标系统和初始观察口(viewport

当首次访问文档时,用户代理(在大多数情况下,即浏览器)确定图像的观察口。观察口是文档实际可见的部分并且由一个坐标系统组成,该坐标系统以左上角的点(0,0)为原点,其正的 x 轴向右而正的 y 轴向下。坐标系统中的一个像素对应观察口中的一个像素。

有几个操作可以创建新的坐标系统。变换(接下来介绍)在被变换元素内部创建新的坐标系统,不过可以通过向文档添加另一个<svg></svg>元素来直接创建新的坐标系统。考虑下面的示例:具有相同x和y 属性的同一元素在不同的位置显示,这是因为第二个元素实际上属于另一个坐标系统,它从第一个元素偏移 100 个像素(cx,cy)

<?xml version="1.0"?>

<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.0//EN"
"http://www.w3.org/TR/2001/REC-SVG-20010904/DTD/svg10.dtd">
<svg width="400" height="200" xmlns="http://www.w3.org/2000/svg">
<desc>Coordinates</desc>
<g>
<ellipse cx="100" cy="100" rx="75" ry="60"
fill="pink" stroke="purple" stroke-width="5"
fill-opacity=".5"/>
<svg x="100" y="0">
<ellipse cx="100" cy="100" rx="75" ry="60"
fill="pink" stroke="purple" stroke-width="5"
fill-opacity=".5"/>
</svg>
</g>
</svg>

3.8 变换

执行变换改变了元素所在的坐标系统,改变了它的外观。变换可以用来以数种方式改变元素的外观:

  • translate(x,y):该变换按指定数量偏移元素。
  • scale(x, y):该变换更改元素的大小。可以分别控制 x 和 y 方向上缩放量,但如果只指定一个值,那么它将用于两个方向。
  • rotate(n):该变换按指定的角度旋转元素。
  • skewX(n)/ skewY(n) :这两种变换根据适当的轴按指定的像素数量偏斜元素。
  • 也可以使用矩阵指定变换

变换是累积的,并且既可以指定为单个变换属性的一部分也可以指定为嵌套元素的一部分

<?xml version="1.0"?>
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.0//EN"
"http://www.w3.org/TR/2001/REC-SVG-20010904/DTD/svg10.dtd">
<svg width="400" height="200" xmlns="http://www.w3.org/2000/svg"
xmlns:xlink="http://www.w3.org/1999/xlink">
<desc>Coordinates</desc>
<defs>
<rect id="refBox" x="0" y="0" height="100" width="100"
stroke="purple" stroke-width="3" fill="none"/>
</defs>
<g>
<!-- Top lines -->
<g transform="scale(1, .5) translate(0, 50)">
<path stroke="purple" stroke-width="3"
d="M25 50 L 125 5 L 225 50" fill="none"/>
</g>
<!-- Left box -->
<use xlink:href="#refBox"
transform="translate(25, 50) skewY(15)"/>
<!-- Right box -->
<g transform="translate(25,25)">
<g transform="skewY(-15)">
<g transform="translate(100, 79)">
<use xlink:href="#refBox"/>
</g>
</g>
</g>
<!-- Text along the side -->
<g transform="rotate(90) translate(0, -250)">
<text font-size="35">Transform!</text>
</g>
</g> </svg>

在这个示例中要注意的可能最重要的事就是正在变换的是实际坐标系统。对象本身实际上没有变换,但它所在的坐标系统中的更改使它看起来发生变化。考虑上面的“Transform!”文本。现在正在沿 y 方向将它平移负 250 个像素,因此显而易见文本应该消失,它会在观察口顶部以上显示。然而在平移发生前,坐标系统进行了 90 度旋转,所以负的 y 值实际上使文本向右移动了 250 个像素。

3.9 用 viewBox 缩放

没有任何更改时,初始观察口指定一个大小,其左上方坐标为 0,0,右下方坐标为介于该大小值与 0,0 之间的像素数目。但有时候期望的效果是按可用的大小(不管大小是多少)而不是按图像进行缩放。那就要用到 viewBox 属性了。

viewBox 属性重新映射观察口,它指定将在观察口左上角和右下角出现的新值。请记住:当在 Web 页面上放置SVG 图形时,<object></object> 标记的尺寸决定观察口的大小。

3.10 路径Path

路径是一系列命令,用来创建作为图像一部分精确定义的形状。该形状可以是开放的(如线)或闭合的(如多边形),并可以包含一条或多条线、曲线和线段。

<svg width="400" height="400" xmlns="http://www.w3.org/2000/svg">
<desc>A simple path</desc>
<rect x="1" y="1" width="350" height="350"fill="none" stroke="blue" />
<path d="M 100 100 L 300 50 L 300 250 L 100 300 Z" fill="red" stroke="blue" stroke-width="3" />
</svg>

上述代码根据提供的指令生成一个简单的多边形。这些指令如下所示:

  • M 100 100 :移至点 100, 100。
  • L 300 50 :画一条线至点 300, 50。
  • L 300 250 :画一条线至点 300, 250。
  • L 100 300 :画一条线至点 100, 300。
  • Z :通过将线画回到原始点闭合此形状。(更具体地说,回到最近一条“move”命令所指定的点。)

请注意这里显示的所有命令都是大写字母,这说明这些坐标是相对于整个坐标系统的绝对坐标。使用小写字母命令则指明为相对坐标。因此命令 l 50 50 从当前点创建一条线至距当前点下方和右方各 50 像素的点,那一点可能在任何位置。

其它简单的线命令包括作水平线的 H (或 h)和作垂直线的 V (或 v

3.11 曲线

路径命令可以创建三种类型的曲线:

  • 椭圆曲线是椭圆的一部分,也称为弧。A (或 a)命令通过指定起点、终点、x 和 y 轴半径、旋度和方向来创建它们。
  • 三次贝塞尔曲线由一个起点、一个终点和两个将曲线“拖”向自己的控制点定义。C (或 c)命令(指定起点和终点)和 S (或 s)命令(假设这条曲线从最近的命令终止的地方继续)创建这些曲线。
  • 二次贝塞尔曲线与其三次贝塞尔曲线类似,不过仅包含一个控制点。Q(或 q)和 T(或 t)命令可以创建这些曲线。

下面的示例显示了一些样本弧,为了清楚除去了文本。弧命令的格式如下:

A radiusX, radiusY rotation large arc flag, sweep flag endX, endY

因此一个半径为 50 和 25,没有旋度, 使用椭圆长轴部分以及曲线的下段,在距起点右边 50 个像素和下方 25 个像素处终止的弧将使用:

a50,25 0 1,0 50,25

<?xml version="1.0" standalone="no"?>
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.0//EN"
"http://www.w3.org/TR/2001/REC-SVG-20010904/DTD/svg10.dtd">
<svg width="4cm" height="4cm" viewBox="0 0 400 400"
xmlns="http://www.w3.org/2000/svg">
<desc>Curved paths</desc>
<rect x="1" y="1" width="398" height="300"
fill="none" stroke="blue" />
<!-- First row -->
<text x="25" y="30">Large arc flag=1</text>
<text x="25" y="45">Sweep flag=0</text>
<text x="25" y="60">Rotation=0</text>
<path d="M75,100 a50,25 0 1,0 50,25"
stroke="blue" stroke-width="5" fill="none" />
<path d="M150,100 a50,25 0 1,1 50,25"
stroke="blue" stroke-width="5" fill="none" />
<path d="M275,100 a50,25 -45 1,1 50,25"a
stroke="blue" stroke-width="5" fill="none" />
<!-- Second row -->
<path d="M100,225 a50,25 0 0,1 50,25"
stroke="blue" stroke-width="5" fill="none" />
<path d="M225,225 a50,25 0 0,0 50,25"
stroke="blue" stroke-width="5" fill="none" />
</svg>

贝塞尔曲线的形状由起点和终点以及控制点的位置确定。这些命令的格式如下:

  • C control1x, control1y, control2x, control2y, endx, endy
  • S control2x, control2y, endx, endy
  • Q controlx, controly, endx, endy
  • T endx, endy

对于 S 和 T 命令,假设第一个控制点为前一条曲线的第二个控制点的反射。

<?xml version="1.0" standalone="no"?>
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.0//EN"
"http://www.w3.org/TR/2001/REC-SVG-20010904/DTD/svg10.dtd"> <svg width="4cm" height="4cm" viewBox="0 0 400 400"
xmlns="http://www.w3.org/2000/svg">
<desc>Curved paths</desc>
<rect x="1" y="1" width="398" height="300"
fill="none" stroke="blue" />
<!-- First row -->
<path d="M75,100 c25,-75 50,50 100,0 s50,-50 150,50"
stroke="blue" stroke-width="5" fill="none" />
<circle cx="175" cy="100" r="5" fill="red" />
<circle cx="75" cy="100" r="5" fill="red" />
<circle cx="325" cy="150" r="5" fill="red" />
<path d="M75,225 q25,-75 100,0 t150,50"
stroke="blue" stroke-width="5" fill="none" />
<circle cx="175" cy="225" r="5" fill="red" />
<circle cx="75" cy="225" r="5" fill="red" />
<circle cx="325" cy="275" r="5" fill="red" />
</svg>

3.12 标记mark

标记是对路径的自然补充。它们是可以添加到线和路径起点、终点和顶点的元素。最常用的是将箭头添加到线的终点,不过可以使用任何对象。

过程很简单:定义标记,然后使用 marker-start、marker-end 和 marker-mid 属性将其赋值给相关元素。

<?xml version="1.0" standalone="no"?>
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.0//EN"
"http://www.w3.org/TR/2001/REC-SVG-20010904/DTD/svg10.dtd">
<svg width="4cm" height="4cm" viewBox="0 0 400 400"
xmlns="http://www.w3.org/2000/svg">
<desc>Markers</desc>
<defs>
<marker id="arrow"
viewBox="0 0 10 10" refX="0" refY="5"
markerUnits="strokeWidth" markerWidth="3" markerHeight="10"
orient="auto">
<path d="M 0 0 L 10 5 L 0 10 z" fill="yellow" stroke="black"/>
</marker>
</defs>
<rect x="1" y="1" width="398" height="300"
fill="none" stroke="blue" />
<!-- First row -->
<path d="M75,100 c25,-75 50,50 100,0 s50,-50 150,50"
stroke="purple" stroke-width="5" fill="none"
marker-start="url(#arrow)"
marker-mid="url(#arrow)"
marker-end="url(#arrow)" />
<!-- Second row -->
<path d="M75,200 c25,-75 50,50 100,0 s50,-50 150,50"
stroke="purple" stroke-width="3" fill="none"
marker-start="url(#arrow)"
marker-mid="url(#arrow)"
marker-end="url(#arrow)" />
</svg>

这个标记本身由一个简单的三角形路径组成,它由标记属性决定。已经设置了 viewBox,以便不管框是什么,标记本身总是会填充整个框。因为 markerUnits 值的缘故,框本身受应用标记线的大小影响。markerUnits 属性也被设置为 userSpaceOnUse,这使标记使用常规坐标系统。refX 和 refY 属性确定标记(该标记“附加”到它所标记的线)内的点。最后,标记的方位设为 auto,使它的 Y 轴与线的切线垂直。(为了理解这一方位,标记构建为指向 X 轴方向)。

请注意标记大小随笔划大小的改变而改变

3.13 添加文本

SVG 的强大能力之一是它可以将文本控制到标准 HTML 页面不可能有的程度,而无须求助图像或其它插件(后者会带来可访问性挑战)。任何可以在形状或路径上执行的操作(如绘制或滤镜)都可以在文本上执行。

一个不足之处是 SVG 不执行自动换行。如果文本比允许空间长,则简单地将它切断。多数情况下,创建多行文本需要多个文本元素。

可以使用 tspan 元素将文本元素分成几部分,允许每部分有各自的样式。在 text 元素中,空格的处理与 HTML 类似;换行和回车变成空格,而多个空格压缩成单个空格,如下面的早期示例所示:

<?xml version="1.0"?>
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.0//EN"
"http://www.w3.org/TR/2001/REC-SVG-20010904/DTD/svg10.dtd">
<svg width="400" height="200" xmlns="http://www.w3.org/2000/svg"
xmlns:xlink="http://www.w3.org/1999/xlink">
<desc>Text</desc>
<defs>
</defs>
<g>
<text x="20" y="50" font-size="30">
Colors can be specified
</text>
<text x="20" y="100" font-size="30">by their
<tspan fill="rgb(255,0,0)">R</tspan>
<tspan fill="rgb(0,255,0)">G</tspan>
<tspan fill="rgb(0,0,255)">B</tspan>
values</text>
<text x="20" y="150" font-size="30">
or by keywords such as
</text>
<text x="20" y="200" font-size="30">
<tspan fill="lightsteelblue">lightsteelblue</tspan>,
</text>
<text x="20" y="250" font-size="30">
<tspan fill="mediumseagreen">mediumseagreen</tspan>,
</text>
<text x="20" y="300" font-size="30">and
<tspan fill="darkorchid">darkorchid</tspan>.
</text>
</g>
</svg>

路径上的文字

在纯 HTML 中不可能具有的一个 SVG 能力是将文本沿路径排列。要实现这一点,需创建一个链接到预定义的路径信息的textPath元素

<?xml version="1.0"?>
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.0//EN"
"http://www.w3.org/TR/2001/REC-SVG-20010904/DTD/svg10.dtd">
<svg width="400" height="300" xmlns="http://www.w3.org/2000/svg"
xmlns:xlink="http://www.w3.org/1999/xlink">
<desc>Text</desc>
<defs>
<path id="wavyPath" d="M75,100 c25,-75 50,50 100,0 s50,-50 150,50"/>
</defs>
<g>
<rect x="1" y="1" width="398" height="200"
fill="none" stroke="blue" />
<text x="50" y="50" font-size="14">
<textPath xlink:href="#wavyPath">
Text travels along any path that you define for it.
</textPath>
</text>
</g>
</svg>

3.14 使用 CSS 属性

实际上,所有的属性(对于所有元素,不仅是文本)都可以用级联样式表与一个元素关联,并且文本的所有 CSS属性都在 SVG 图像中可用。

可以直接用样式属性设计元素的样式,或者引用样式表设计元素的样式。

<?xml version="1.0"?>
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.0//EN"
"http://www.w3.org/TR/2001/REC-SVG-20010904/DTD/svg10.dtd">
<svg width="400" height="300" xmlns="http://www.w3.org/2000/svg"
xmlns:xlink="http://www.w3.org/1999/xlink">
<desc>Text</desc>
<defs>
<style type="text/css">
<![CDATA[
.abbreviation { text-decoration: underline; }
]]>
</style>
</defs>
<g>
<text x="20" y="50" font-size="30">
Colors can be specified
</text>
<text x="20" y="100" font-size="30">by their
<tspan fill="rgb(255,0,0)" class="abbreviation">R</tspan>
<tspan fill="rgb(0,255,0)" class="abbreviation">G</tspan>
<tspan fill="rgb(0,0,255)" class="abbreviation">B</tspan>
values</text>
<text x="20" y="150" font-size="30">
or by keywords such as
</text>
<text x="20" y="200" font-size="30">
<tspan fill="lightsteelblue" style="fill: lightsteelblue; font-size:20">lightsteelblue</tspan>,
</text>
<text x="20" y="250" font-size="30">
<tspan fill="mediumseagreen">mediumseagreen</tspan>,
</text>
<text x="20" y="300" font-size="30">and
<tspan fill="darkorchid">darkorchid</tspan>.
</text>
</g>
</svg>

四、SVG DOM——使用javascript访问SVG元素

由于SVG是html的元素,所以支持普通的DOM操作,又由于SVG本质上是xml文档,所以也有一种特殊的DOM操作,大多称之为SVG DOM。

说明: 当然了,由于目前IE不支持SVG,开发基于IE的SVG页面需要采用不同的方式。

与普通的html元素的DOM操作完全一样:

  • 选择元素:document.getElementById
  • 创建元素:document.createElementNS
  • 创建子元素的另外一种方式:element.createChildNS
  • 添加元素:node.appendChild
  • 设置元素的属性:element.setAttributeNS/element.setAttribute

除了上面这几个操作,下面的操作和属性也很常见:

  • 获取元素的属性值: element.getAttributeNS/element.getAttribute
  • 检查元素是否存在某属性:element.hasAttributeNS
  • 移除元素的某属性:element.removeAttributeNS
    <?xml version="1.0" encoding="UTF-8" standalone="no"?>
    <!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.0//EN" "http://www.w3.org/TR/2001/REC-SVG-20010904/DTD/svg10.dtd">
    <svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" width="300" height="300">
    <script type="text/ecmascript">
    <![CDATA[
    function removeFill(evt)
    {
    //通过获得元素,
    var element = evt.target;
    if (element.hasAttributeNS(null,"fill"))
    {
    element.removeAttributeNS(null,"fill");
    }
    else
    {
    alert("This element doesn't have a fill attribute.");
    }
    }
    ]]>
    </script>
    <g id="firstGroup">
    <rect width="70" height="50" x="40" y="5" fill="blue" onclick="removeFill(evt)"/>
    <rect width="70" height="50" x="40" y="65" fill="blue" onclick="removeFill(evt)"/>
    <rect width="70" height="50" x="40" y="125" fill="blue" onclick="removeFill(evt)"/>
    <rect width="70" height="50" x="40" y="185" fill="blue" onclick="removeFill(evt)"/>
    <rect width="70" height="50" x="40" y="245" fill="blue" onclick="removeFill(evt)"/>
    <text x="150" y="30">Click on rectangle
    <tspan x="150" dy="15">to remove it's color.</tspan>
    </text>
    </g>
    </svg>
  • 父元素、子元素和兄弟节点:element.parentNode/element.firstChild/child.nextSibling
    <?xml version="1.0" encoding="UTF-8" standalone="no"?>
    <!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.0//EN" "http://www.w3.org/TR/2001/REC-SVG-20010904/DTD/svg10.dtd">
    <svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" width="300" height="100">
    <script type="text/ecmascript">
    <![CDATA[
    function showContentAndRelatives(evt)
    {
    //get reference to text element
    var textElement = document.getElementById("myText");
    //get reference to parent of element
    var parent = textElement.parentNode;
    alert("the parent node has id '"+parent.getAttributeNS(null,'id')+"'\nNodename is '" +parent.nodeName+"'");
    //get a reference to the first child of the element "myText"
    var child = textElement.firstChild; //loop over all childs
    while (child != null)
    {
    //see if child is a tspan and has child nodes
    if (child.nodeName == "tspan" && child.hasChildNodes())
    {
    //see if firstChild is of nodeType "text"
    if (child.firstChild.nodeType == 3)
    {
    alert("child's text content="+child.firstChild.nodeValue);
    }
    }
    child = child.nextSibling;
    }
    alert("the content of the second tspan Child is: "+textElement.childNodes.item(1).firstChild.nodeValue);
    }
    ]]>
    </script>
    <g id="firstGroup">
    <text id="myText" x="50" y="30" onclick="showContentAndRelatives(evt)">
    <tspan>Click on text</tspan>
    <tspan x="50" dy="15">to get parent node data</tspan>
    <tspan x="50" dy="15">and to see the text node values </tspan>
    <tspan x="50" dy="15">of each line</tspan>
    </text>
    </g>
    </svg>

需要注意的是SVG本质上是XML文档,所以基本采用的DOM方法都是带NS结尾的方式,来提供相关的namespace;如果创建元素时已经提供 了namespace,而且没有多个namespace的问题,那么设置相关属性的时候,也可以选择使用不带NS的版本,比如直接使用 element.setAttribute设置属性值,但是总的来说,还是强烈推荐使用带NS结尾的版本,因为这个版本总是工作正常的,即使是在多 namespace的情况下。

1、SVG DOM的一般操作方法

上面的例子中,我们使用element.setAttributeNS/element.setAttribute来给属性赋值,在SVG DOM中,可以使用面向对象的方式,通过访问点号来给对象的属性赋值,比如

  • 普通的DOM方式:
element.setAttribute("x", "10");
element.setAttribute("y", "20");
element.setAttribute("width", "100%");
element.setAttribute("height", "2em");
  • SVG DOM的方式:
element.x.baseVal.value = 10;
element.y.baseVal.value = 20;
element.width.baseVal.newValueSpecifiedUnits(SVGLength.SVG_LENGTHTYPE_PERCENTAGE, 100);
element.height.baseVal.newValueSpecifiedUnits(SVGLength.SVG_LENGTHTYPE_EMS, 10);

DOM脚本属于传统的脚本,其特征是通过构建“值字符串”来设置各个项。SVG DOM脚本样式的优点是,你不必构建“值字符串”,所以性能优于DOM脚本。

<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml" >
<head>
</head>
<body>
<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" width="300" height="300">
<script type="text/ecmascript">
<![CDATA[
function showRectColor() {
alert(document.getElementById("myBlueRect").getAttributeNS(null,"fill"));
} function showRectArea(evt) {
var width = parseFloat(evt.target.getAttributeNS(null,"width"));
var height = parseFloat(evt.target.getAttributeNS(null,"height"));
alert("The rectangle area is: " + (width * height));
} function showRootChildrenNr() {
alert("Nr of Children: "+document.documentElement.childNodes.length);
}
]]>
</script>
<g id="firstGroup">
<rect id="myBlueRect" width="100" height="50" x="40" y="20" fill="blue" onclick="showRectArea(evt)"/>
<text x="40" y="100" onclick="showRectColor()">Click on this text to show rectangle color.</text>
<text x="40" y="130">Click on rectangle to show rectangle area.</text>
<text x="40" y="160" onclick="showRootChildrenNr()">Click on this text to show the number of child
<tspan x="40" dy="20">elements of the root element.</tspan></text>
</g>
</svg>
</body>
</html>

在这个例子中,列举了常见的获取DOM对象的方式:

  1. 通过document.getElementById或者document.getElementByClassName之类的方法获取对象;
  2. 通过document.documentElement或者document.rootElement获取document对象;
  3. 通过事件参数evt.target获取产生事件的对象。这种方式的优点就是不使用id就可以获取到产生事件的对象。

其余的脚本基本和普通的DOM是一样的。

2、svg DOM的一些js操作

2.1)Circle

var svgns = "http://www.w3.org/2000/svg";
function makeShape(evt) {
if ( window.svgDocument == null )
svgDocument = evt.target.ownerDocument; var shape = svgDocument.createElementNS(svgns, "circle");
shape.setAttributeNS(null, "cx", 25);
shape.setAttributeNS(null, "cy", 25);
shape.setAttributeNS(null, "r", 20);
shape.setAttributeNS(null, "fill", "green"); svgDocument.documentElement.appendChild(shape);
}

circle

2.2)Ellipse

var svgns = "http://www.w3.org/2000/svg";

function makeShape(evt) {
if ( window.svgDocument == null )
svgDocument = evt.target.ownerDocument; var shape = svgDocument.createElementNS(svgns, "ellipse");
shape.setAttributeNS(null, "cx", 25);
shape.setAttributeNS(null, "cy", 25);
shape.setAttributeNS(null, "rx", 20);
shape.setAttributeNS(null, "ry", 10);
shape.setAttributeNS(null, "fill", "green"); svgDocument.documentElement.appendChild(shape);
}

Ellipse

2.3)Line

var svgns = "http://www.w3.org/2000/svg";

function makeShape(evt) {
if ( window.svgDocument == null )
svgDocument = evt.target.ownerDocument; var shape = svgDocument.createElementNS(svgns, "line");
shape.setAttributeNS(null, "x1", 5);
shape.setAttributeNS(null, "y1", 5);
shape.setAttributeNS(null, "x2", 45);
shape.setAttributeNS(null, "y2", 45);
shape.setAttributeNS(null, "stroke", "green"); svgDocument.documentElement.appendChild(shape);
}

Line

2.4)Path

var svgns = "http://www.w3.org/2000/svg";

function makeShape(evt) {
if ( window.svgDocument == null )
svgDocument = evt.target.ownerDocument; var shape = svgDocument.createElementNS(svgns, "path");
shape.setAttributeNS(null, "d", "M5,5 C5,45 45,45 45,5");
shape.setAttributeNS(null, "fill", "none");
shape.setAttributeNS(null, "stroke", "green"); svgDocument.documentElement.appendChild(shape);
}

Path

2.5) Polygon

var svgns = "http://www.w3.org/2000/svg";

function makeShape(evt) {
if ( window.svgDocument == null )
svgDocument = evt.target.ownerDocument; shape = svgDocument.createElementNS(svgns, "polygon");
shape.setAttributeNS(null, "points", "5,5 45,45 5,45 45,5");
shape.setAttributeNS(null, "fill", "none");
shape.setAttributeNS(null, "stroke", "green"); svgDocument.documentElement.appendChild(shape);
}

Polygon

2.6) Polyline

var svgns = "http://www.w3.org/2000/svg";

function makeShape(evt) {
if ( window.svgDocument == null )
svgDocument = evt.target.ownerDocument; shape = svgDocument.createElementNS(svgns, "polyline");
shape.setAttributeNS(null, "points", "5,5 45,45 5,45 45,5");
shape.setAttributeNS(null, "fill", "none");
shape.setAttributeNS(null, "stroke", "green"); svgDocument.documentElement.appendChild(shape);
}

Polyline

2.7)Rectangle

var svgns = "http://www.w3.org/2000/svg";

function makeShape(evt) {
if ( window.svgDocument == null )
svgDocument = evt.target.ownerDocument; var shape = svgDocument.createElementNS(svgns, "rect");
shape.setAttributeNS(null, "x", 5);
shape.setAttributeNS(null, "y", 5);
shape.setAttributeNS(null, "width", 40);
shape.setAttributeNS(null, "height", 40);
shape.setAttributeNS(null, "fill", "green"); svgDocument.documentElement.appendChild(shape);
}

Rectangle

2.8)Rounded Rectangle

var svgns = "http://www.w3.org/2000/svg";

function makeShape(evt) {
if ( window.svgDocument == null )
svgDocument = evt.target.ownerDocument; var shape = svgDocument.createElementNS(svgns, "rect");
shape.setAttributeNS(null, "x", 5);
shape.setAttributeNS(null, "y", 5);
shape.setAttributeNS(null, "rx", 5);
shape.setAttributeNS(null, "ry", 5);
shape.setAttributeNS(null, "width", 40);
shape.setAttributeNS(null, "height", 40);
shape.setAttributeNS(null, "fill", "green"); svgDocument.documentElement.appendChild(shape);
}

Rounded Rectangle

2.9)Use

var svgns   = "http://www.w3.org/2000/svg";
var xlinkns = "http://www.w3.org/1999/xlink"; function makeShape(evt) {
if ( window.svgDocument == null )
svgDocument = evt.target.ownerDocument; var svgRoot = svgDocument.documentElement; var defs = svgDocument.createElementNS(svgns, "defs"); var rect = svgDocument.createElementNS(svgns, "rect");
rect.setAttributeNS(null, "id", "rect");
rect.setAttributeNS(null, "width", 15);
rect.setAttributeNS(null, "height", 15);
rect.setAttributeNS(null, "style", "fill: green"); defs.appendChild(rect); var use1 = svgDocument.createElementNS(svgns, "use");
use1.setAttributeNS(null, "x", 5);
use1.setAttributeNS(null, "y", 5);
use1.setAttributeNS(xlinkns, "xlink:href", "#rect"); use2 = svgDocument.createElementNS(svgns, "use");
use2.setAttributeNS(null, "x", 30);
use2.setAttributeNS(null, "y", 30);
use2.setAttributeNS(xlinkns, "xlink:href", "#rect"); svgRoot.appendChild(defs);
svgRoot.appendChild(use1);
svgRoot.appendChild(use2);
}

Use

3、实际案例

3.1 进度条

<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title> 进度条 </title>
<script language='JavaScript'>
/* <![CDATA[ */
function ProgressBar(info){
var stem = {};// 此函数最后返回的代表进度条的对象。
var done = 0, length, outline, bar;// 声明内部变量
bar = document.getElementById('done');// 进度条中绿色的变化部分。
length = 80; // 重置进度到零。
function reset(){
return to(0);
}
// 设置进度到某个值。
function to(value){
if (value >= 100) {
done = 100;
bar.setAttribute('width', length);
}
else {
done = value;
bar.setAttribute('width', Math.round(done * length / 100));
}
return stem;
}
// 进度变化某个值。
function advance(step){
return to(done + step);
}
// 以下给进度条对象添加方法。
// 获得当前进度值。
stem.getDone = function(){
return done
};
stem.reset = reset;
stem.to = to;
stem.advance = advance;
return stem;// 返回可供脚本使用的进度条对象。
}
// 测试进度条对象。
function testBar(){
var bar = ProgressBar();
// 此内部函数每运行一次,增加进度值 1,直到进度值为 100。
function test(){
if (bar.getDone() === 100) {
clearInterval(id);
}
else {
bar.advance(1);
}
}
// 每十分之一秒改变一次进度。
var id = setInterval(test, 100);
}
// 页面载入后开始测试。
window.addEventListener('load', testBar, true);
/* ]]> */
</script>
</head>
<body>
<div id='svgDiv'>
<svg xmlns="http://www.w3.org/2000/svg" version="1.1"
viewBox="0 0 100 100" style="border:1px solid; width:100px; height:100px; ">
<g id='progBar'>
<rect x='10' y='45' width='80' height='10' stroke='grey' fill='white'/>
<rect id='done' x='10' y='45' width='0' height='10' fill='green'/>
</g>
</svg>
</div>
</body>
</html>

说明:

<svg xmlns="http://www.w3.org/2000/svg" version="1.1" viewBox="0 0 100 100" style="border:1px solid; width:100px; height:100px; ">,在 XHTML 中直接插入 svg 元素,并指定命名空间等其他属性。

  • viewBox 定义矢量图可见的坐标空间,四个数字依次是原点的 x 坐标、y 坐标、平面的宽度、高度。SVG 的坐标空间符合计算机中指定屏幕空间的惯例,x 坐标轴的正方向向右,y 坐标轴的正方向向下。
  • style 属性指定 svg 元素的各种外观特性。SVG 与 HTML 一样,可以应用 CSS 定义外观,并且有一些专门的特性:
    1. XHTML 中的 JavaScript 代码被包含在 /* <![CDATA[ */ 和 /* ]]> */ 之间。

      在 HTML 文件中不需要这样做。因为在 HTML 中 <script> 标签内的 JavaScript 代码被解释为 CDATA(Character Data,XML 中的一种类型,用于包含任意的字符数据);而在 XHTML 中 <script> 标签内的部分被解释为 PCDATA(Parsed Character Data,也是 XML 中的一种类型,为字符数据和元素的混合内容),所以也要通过 XML 的语法检查,而 JavaScript 代码显然不符合 XML 的标签的定义语法。解决方法就是在代码外人工加上 ![CDATA[ 和 ]]> 标注,使得 XML 的语法校验器忽略这段内容。但是这样会带来第二个问题,有些浏览器不认识 CDATA 标注,因而这些代码又无法通过 JavaScript 的语法检查。所以我们在 CDATA 标注两侧再加上 JavaScript 的注释标记。这样 <script> 标签内的代码既能通过 XML 的语法检查,又能被 JavaScript 引擎认识。

    2. <svg> 标签内有一个 <g> 标签和两个 <rect> 标签。   

      g 元素用于分组。分组不仅可以使 SVG 的内容结构清晰,同一组内的对象还可以被集体操作。rect 元素代表一个矩形;x、y、width 和 height 属性分别指定矩形左上顶点的横坐标、纵坐标和矩形的宽度、长度;stroke 属性指定图形外框的线条颜色。我们用第一个空心的矩形显示进度条的外框,第二个实心的绿色矩形显示变化的进度。为了在脚本中方便地访问,我们设置了绿色矩 形的 id 属性。

    3. 代码说明   

      在 JavaScript 脚本中我们用 DOM 先后获得绿色矩形对象并修改它的宽度属性。getElementById 和 setAttribute 的用法和在 HTML 中没有两样。值得注意的是,有些我们在操作 HTML 时使用的方法,在 XML 中是不存在的,如根据名称获取元素的 getElementsByName。

      这个例子中前三点特别的设定有些麻烦,不过这些在正在获得越来越多支持并且很快将成为互联网的现实标准的 HTML 5 中都是不必要的。在 HTML 5 中不需要在 html 和 svg 元素中指定命名空间,svg 和其中的各种标签会被自动识别。JavaScript 代码也会和在现在的 HTML 页面中一样,不需要在两侧加上 CDATA 标注。

3.2 模拟控件

事件——SVG 中的元素同样支持用户界面的事件。因此我们可以通过鼠标、键盘触发的各种事件改变 SVG 中的图形。这就使得在整个页面上可以进行丰富的图形的互动,而不需要借助于 Flash 插件。下面通过几个例子来说明对事件的运用,使用的都是 DOM3 事件规范中定义的方法。

HTML 中的单选钮、复选框、下拉列表等标准控件为用户输入和显示数据提供了各种友好的方式。过去我们只能“使用”它们,现在我们可以模拟甚至创造新的控件。我们先来模拟一个简单的单选钮的图形和行为。

1)模拟单选钮响应鼠标点击的外观

<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title> 模拟单选钮 1</title>
<script language='JavaScript'>
/* <![CDATA[ */
function mimicRadio(){
var radName = 'pRadio', selectedId = 'pRadioSelected';
var ns = 'http://www.w3.org/2000/svg';//SVG 元素的命名空间。
var circles = document.getElementsByTagNameNS(ns, 'circle');
var selected = document.getElementById(selectedId);// 用于模拟选中状态的实心圆。
var circle;
for (var i = 0; i < circles.length; i++) {
circle = circles[i];
if (circle.getAttribute('name') === radName) {
// 上面提到过,在 XML 的 DOM 中,没有 getElementsByName 方法,所以我们需要手工检查元素的 name 属性。
// 为 circle 元素添加鼠标响应。
// 通过设置实心圆的位置和 style 中的 display 值,模拟单选钮被选中的行为。
circle.addEventListener('click', function(){
selected.setAttribute('cx', this.getAttribute('cx'));
selected.setAttribute('cy', this.getAttribute('cy'));
selected.style.display = 'block';
}, true)
}
}
}
window.addEventListener('load', mimicRadio, true);
/* ]]> */
</script>
</head>
<body>
<div id='svgDiv'>
<svg xmlns="http://www.w3.org/2000/svg" version="1.1" viewBox="0 0 120 100"
style="border:1px solid; width:120px; height:100px; ">
<circle name='pRadio' cx="20" cy="50" r="6" stroke="black"
stroke-width='0.5' fill="white" />
<text x='28' y='53'>
上升
</text>
<circle name='pRadio' cx="70" cy="50" r="6" stroke="black"
stroke-width='0.5' fill="white"/>
<text x='78' y='53'>
下降
</text>
<circle id='pRadioSelected' cx="20" cy="50" r="2" style='display:none;'/>
</svg>
</div>
</body>
</html>

2)加入读和写数据操作的模拟单选钮

<!DOCTYPE html>
<html>
<head>
<title>模拟单选钮2</title>
<script language='JavaScript'>
/* <![CDATA[ */
//创建一个可独立使用的模拟的单选钮
function RadioButton(id){
var radName = 'pRadio', markName = 'mark';
var stem = {};
var checkedIndex = -1, value;
var item, items = [], mark;
var ns = 'http://www.w3.org/2000/svg';
//给各个单选钮添加鼠标单击的事件处理程序。
function init(){
var rad = document.getElementById(id);
var circles = rad.getElementsByTagNameNS(ns, 'circle');
for (var i = 0, circle; i < circles.length; i++) {
circle = circles[i];
if (circle.getAttribute('name') === radName) {
items.push(circle);
circle.addEventListener('click', function(){
//这里的index是根据各个单选钮在svg中出现的先后顺序。
setIndex(items.indexOf(this));
}, true)
}
else
if (circle.getAttribute('name') === markName) {
mark = circle;
}
}
}
//选中某项后,更新数据和显示。
function setIndex(index){
checkedIndex = index;
var item = items[index];
value = item.getAttribute('value');
mark.setAttribute('cx', item.getAttribute('cx'));
mark.setAttribute('cy', item.getAttribute('cy'));
mark.style.display = 'block';
}
//供在脚本中选择某项使用,相当于在HTML中设置某个单选钮的checked属性。
stem.setCheckedIndex = function(index){
if (index > -1 && index < items.length) {
setIndex(index);
}
}
//读取选中项的index,相当于在HTML中读取某个单选钮的checked属性。
stem.getCheckedIndex = function(){
return checkedIndex;
}
//读取选中项的值。
stem.getValue = function(){
return value;
} init();
return stem;
}
//测试模拟的单选钮。
function testRadio(){
//获取用脚本和SVG模拟的单选钮。
var rad = RadioButton('radColor');
//测试选中第二项。
rad.setCheckedIndex(1);
//使用一个按钮,显示单选钮选中项的序号和值。
document.getElementById('testRadio').addEventListener('click', function(){
var msg = 'index: ' + rad.getCheckedIndex() + ' value: ' + rad.getValue();
document.getElementById('message').innerHTML = msg;
}, true)
} window.addEventListener('load', testRadio, true);
/* ]]> */
</script>
</head>
<body>
模拟单选钮
<br/>
<input type='button' id='testRadio' value='显示单选钮的选中项'/>
<br/>
<div id="message">
</div>
<div id='svgDiv'>
<svg xmlns="http://www.w3.org/2000/svg" version="1.1" viewBox="0 0 100 100" style="border:1px solid; width:100px; height:100px; ">
<g id='radColor'>
<circle name='pRadio' cx="20" cy="20" r="6" stroke="black" stroke-width='0.5' fill="white" value='red'/>
<text x='28' y='26'>
红色
</text>
<circle name='pRadio' cx="20" cy="50" r="6" stroke="black" stroke-width='0.5' fill="white" value='green'/>
<text x='28' y='56'>
绿色
</text>
<circle name='pRadio' cx="20" cy="80" r="6" stroke="black" stroke-width='0.5' fill="white" value='blue'/>
<text x='28' y='86'>
蓝色
</text>
<circle name='mark' cx="20" cy="20" r="2" style='display:none;'/>
</g>
</svg>
</div>
</body>
</html>

3.3 笑脸动画

一个圆、两个点和一段弧线构成的表情是互联网上著名的符号。这个简单的图形不仅可以轻易被看成一张脸,而且通过改变弧线的形状, 还可以表现从笑脸到哭脸的不同表情。它显示了人脑识别人脸的复杂行为是以模块和模糊的方式进行的,这种抽象的处理方式也为展现 SVG 的功能提供了一个良好的场合。我们先画出这样一张高度简化的脸,再创建一个滑动条控件,最后用拖动滑动条的方式来控制脸中弧线的弯曲程度和方向。如果把滑 块的高度解释成象征一个人的压力,那么我们就看到了随着压力的改变,人的表情是如何变化的。此代码较长,可以在参考资源中下载。

<html xmlns="http://www.w3.org/1999/xhtml">
<head>
<title>Graph Demo</title>
<script language='JavaScript'>
/* <![CDATA[ */ function Slider(id){
var sbId='pressure', faceId='face';
var ns = 'http://www.w3.org/2000/svg';
var svg, sliderBar, slider, bar, face, mouth;
var pressed = false; //滑块是否被鼠标按下
var offsetY=0; //鼠标按下的位置与滑块在纵坐标上的差值
//初始化滑动条各个组件
function init(){
face=document.getElementById(faceId);
mouth=face.getElementsByTagNameNS(ns, 'path')[0];
sliderBar = document.getElementById(sbId);
svg = sliderBar.ownerSVGElement;
var rects=sliderBar.getElementsByTagNameNS(ns, 'rect');
for (var i=0, rect; i<rects.length; i++){
rect=rects[i];
if (rect.getAttribute('name')==='bar'){
bar=rect;
}else if(rect.getAttribute('name')==='slider'){
slider=rect;
}else{
break;
}
}
}
//将一个点从屏幕坐标转换到SVG用户坐标。
//需要转换的点的坐标从事件参数event中获得。
function toUserCor(event){
//getScreenCTM方法获得的是将一个点从SVG用户坐标转换到屏幕坐标所需的转换矩阵。
//背后是一些有趣的关于平面坐标变换和矩阵的数学。
var m = svg.getScreenCTM();
var p = svg.createSVGPoint();
p.x = event.clientX;
p.y = event.clientY;
//matrixTransform将一个点按照参数中的矩阵做坐标变换。
//因为m是将一个点从SVG用户坐标转换到屏幕坐标所需的转换矩阵,所以需要先用inverse方法获得此矩阵的逆阵。
p = p.matrixTransform(m.inverse());
return p;
} //添加事件响应程序
function addListeners(){
//鼠标悬浮时,高亮(将颜色设为白色)滑块。
slider.addEventListener('mouseover', function(){
//Firefox不支持用脚本访问SVG专门的样式属性this.style.fill = 'white';
this.setAttribute('fill', 'white');
}, false);
//鼠标退出时,恢复滑块的颜色。
slider.addEventListener('mouseout', function(){
if (!pressed) {
this.setAttribute('fill', 'grey');
}
}, false);
//鼠标按下时,记录下鼠标状态和其与滑块在纵坐标上的差值,高亮滑块。
slider.addEventListener('mousedown', function(event){
pressed = true;
var p = toUserCor(event);
var sliderBox=slider.getBBox();
offsetY=sliderBox.y-p.y;
this.setAttribute('fill', 'white');
}, false);
//鼠标键松开时,更改状态。
//如果鼠标已移出滑块的范围,恢复滑块的颜色。
svg.addEventListener('mouseup', function(event){
pressed = false;
if (!isInBox(toUserCor(event), slider.getBBox())) {
slider.setAttribute('fill', 'grey');
}
}, false);
//鼠标移动时,如果鼠标键是按下的,更新滑块的位置,并且修改脸中的弧线。
svg.addEventListener('mousemove', function(event){
if (pressed) {
var p = toUserCor(event);
var barBox=bar.getBBox();
var sliderBox=slider.getBBox();
var sliderY=p.y + offsetY;
if (sliderY>= barBox.y && sliderY<= barBox.y + barBox.height - sliderBox.height) {
slider.setAttribute('y', sliderY);
//d='M-18,18 Q0,-2 18,18'
//滑动条高度为100,中点纵坐标为-4,笑脸控制点纵坐标变化范围为-2到38。
var y=Math.round(((sliderY-(-4))/50)*20+18);
mouth.setAttribute('d', 'M-18,18 Q0,'+y+' 18,18');
}
}
}, false);
//DOM Level 3还没有列出与拖放相关的事件,但是Firefox(其他浏览器如IE也类似)已经实现了。
//如果不取消拖放,则当鼠标拖动滑块时,有时会出现拖放无效的提示,并导致鼠标键释放的事件无法被监听到。
svg.addEventListener('dragstart',
function(event){
event.preventDefault();
event.stopPropagation();
}, false)
} init();
addListeners();
} //检查一个点是否在某个Bouding Box以内。
function isInBox(point, box){
return point.x >= box.x &&
point.x <= box.x + box.width &&
point.y >= box.y &&
point.y <= box.y + box.height;
} //测试
function test(){
var s = Slider('pressure');
} window.addEventListener('load', test, true);
/* ]]> */
</script>
</head>
<body>
<div id="message">
</div>
<div id='svgDiv'>
<svg xmlns="http://www.w3.org/2000/svg" version="1.1" viewBox="-100 -100 200 200" style="border:1px solid; width:200px; height:200px; ">
<!--脸的各个部件被归于id为face的组中-->
<g id='face'>
<!--脸的“背景”,一个黄色的圆-->
<circle cx="0" cy="0" r="50" stroke="black" stroke-width='0.2' fill="yellow" />
<!--两个黑色的实心的小圆作为眼睛-->
<circle cx='18' cy='-18' r='3'/>
<circle cx='-18' cy='-18' r='3'/>
<!--一段贝塞尔曲线作为嘴-->
<path name='mouth' d='M-18,18 Q0,-2 18,18' style='stroke:black; fill:none;'/>
</g>
<!--滑动条的各个部件被归于id为pressure的组中-->
<g id='pressure'>
<!--第一个矩形作为滑动条的外框-->
<rect name='bar' x='70' y='-54' width='8' height='108' style='stroke:black; stroke-width:0.5; fill:white;'/>
<!--第二个灰色实心矩形作为滑块-->
<rect name='slider' x='64' y='-54' width='20' height='8' fill='grey' style='stroke:black; stroke-width:0.5;'/>
</g>
</svg>
</div>
</body>
</html>

参考: