SVG——新一代Web设计及互动媒体的革新

SVG JavaScript脚本编程和无脚本的脚本

来源:IBM developerWorks 中国
英文:http://www.ibm.com/developerworks/xml/library/x-matters42/?S_TACT=105AGX52&S_CMP=cn-a-x

SVG JavaScript脚本  可伸缩矢量图(Scalable Vector Graphics,SVG)浏览器现在变得越来越主流,并且它们可以用于多种强大的方式。继续David的早期SVG专栏,Dethe介绍了一些方式,即声明性的标记可以为动画和其他常见脚本编程任务取代脚本。期间,他简要地介绍了用其他XML方言中的声明性标记来取代脚本。将常见的脚本编程任务移入声明性标记可以不再需要冗长的样本(boilerplate)代码,减少错误,并且使得脚本的意图更加容易理解,从而使您能够专注于代码的惟一方面。

  现在,SVG成为W3C推荐已经有一段时间了,但是一直没有快速扩展到更广的Web领域。这种现状现在可能要改变了,因为Mozilla(Firefox)、Apple(Safari)和Opera都在它们的浏览器中支持(或者将要支持)SVG,而不需要插件。Linux桌面Gnome和KDE也支持将SVG用于主题、图标、背景和游戏中。直到现在,查看SVG的主要方式依然是使用Adobe插件(参见参考资料),我使用该插件测试了示例代码。
  到底什么是SVG,何时适合使用它呢?SVG被拿来跟PDF、PostScript、Flash和HTML作过比较,但是它具有所有这些的元素,却没有重复它们。SVG很复杂,这使得它在保留文本格式(Google可以对它进行索引)和XML(您可以用DOM来操纵它)的同时,对于很多不同的人是不同的东西。在最基本的层次上,它是一种用于描述矢量图的XML语言。矢量图是由绘图线条构成的图形,与位图图形相对,后者是由绘图像素构成的图形。但是SVG也可以包含和操纵位图、文本、甚至声音文件。此外,它还可以:

  • 描述将噪音和其他效果应用于矢量图像或位图图像的方式
  • 指定精细的颜色梯度和模式
  • 提供到Web其他部分的链接
  • 成为脚本的和转换的、动画的和样式的
  • 以多种方式相互交互

  我想要通过讨论SVG动画和交互性,具体来说就是它的声明性动画和交互性,来继续David的关于SVG的早期XML Matters专栏。
  注意:为了查看本文中的SVG文档,您需要一个SVG查看器,在参考资料中可以找到该查看器。您也可以下载一个.zip文件,其中包含有所有与SVG相关的文件。

1 声明性编程101

  声明性编程更多地像是您想要发生的事情的描述,而不用关心如何使之发生的细节。声明性编程的常见例子包括电子表格、SQL、XSLT和诸如Haskell这样的编程语言。其他混合提供声明性和过程性构造的编程语言包括Lisp和Python。几个XML方言将包括更多声明性特性,以便为处理诸如格式验证(XForms)、数据挖掘(XQuery)、转换(XSLT)、事件处理(XMLEvents)和动画(Synchronized Multimedia Integration Language或SMIL)之类的任务而取代更复杂和/或重复的JavaScript。
  SMIL(参见参考资料)是一种用于实时地混合视频、图形、音乐和文本的语言,已被SVG所采纳和扩展。SVG中的大部分声明性动画都直接取自SMIL(不用使用SMIL名称空间),尽管SMIL的有些方面被省去(因为它们在SVG的上下文中没有任何意义),另外一些方面则被扩展。SMIL被用于RealNetworks的RealPlayer中、Apple的QuickTime中,以及(作为XHTML+SMIL)Microsoft的Internet Explorer中。SMIL是一种工具,用于编排这些不同的媒体格式,以响应时间和事件。
  SVG继承自SMIL的声明性能力让您可以使图片成为动画,以预定的次数播放,或者响应某些事件。您可以声明五种类型的动画,以及使用许多事件来触发您的动画。
  现在该向您展示一些例子了。

2 绘制空白

  第一个例子为本文中其余的例子创建一个画布。我将回到小时候,使用火箭和UFO的草图,所以背景是一张简单的横线笔记簿纸张,上面有三个洞。这个例子没有使用动画。我只是设置一个画布,像是小时候用的笔记簿纸张,用于涂鸦。纸张非常简单,其中只有一个svg元素,该元素包含一小段定义:一个线条模式和一个小圆。然后,在代码体中(defs之外),我在整个页面上绘制了一个矩形,用线条模式进行填充。然后,我利用刚才定义的圆,在三个地方画了三个圆圈。代码1展示了笔记簿纸张例子的SVG代码。

  代码1 一张笔记簿纸张

 

<?xml version="1.0" encoding="utf-8"?>
<svg xmlns="http://www.w3.org/2000/svg"
xmlns:xlink="http://www.w3.org/1999/xlink"
width="100%" height="100%" fill="white">
<defs>
<pattern id="single_line" x="0" y="0" width="1" height="32"
patternUnits="userSpaceOnUse" viewBox="0 0 1 32">
<line x1="0" y1="32" x2="1" y2="32"
stroke="lightblue" stroke-width="0.5"/>
</pattern>
<circle id="single_hole" width="28" height="28"
cx="14" cy="14" r="14"
fill="white" stroke="black" stroke-width="0.5"/>
</defs>
<rect x="0" y="0" width="100%" height="100%"
fill="url(#single_line)" stroke="none"/>
<use xlink:href="#single_hole" x="32" y="10%"/>
<use xlink:href="#single_hole" x="32" y="50%"/>
<use xlink:href="#single_hole" x="32" y="90%"/>
</svg>

 

  要在您的浏览器中运行这个例子,请下载示例代码(即下载一节中的01_paper.svg)。如果已经安装好了SVG查看器,则单击查看这个例子。

3 火箭科学

  第二个例子向定义中添加了一个path元素,并在svg的代码体中使用了一次该元素(参见代码2)。路径由一个移动语句(M xy)和一些以绝对坐标表示的line-to语句(Lx y)组成。(使用相对坐标的语句应该是小写的。)d属性包含1,072个语句(通过Wacom写字板收集而来),所以我将在代码2中简化它。您可以在示例代码(参见下载一节中的02_rocket_static.svg)中查看完整的文件)。

  代码2 一个宇宙飞船(只包含更改)

 

<defs>
[...]
<path id="rocket" stroke-width="3" stroke-linejoin="round"
stroke-linecap="round" stroke="#4C4C4C" fill="white"
d="M23 8 L21 11 L21 11 L20 16 [etc...]"/>
</defs>
[...]
<use x="50%" y="50%" xlink:href="#rocket"/>

 

  到目前为止,我的涂鸦还是与虚拟的纸张一样是静态的(参见图1),但是下一个例子中将改变这种情况。

火箭

图1 火箭

  要在您的浏览器中运行这个例子,请下载示例代码(参见下载一节中的内容)。如果已经安装好了SVG查看器,则单击查看图形。

4 点火起飞

  终于,现在该运动了。火箭将沿着一个圆圈移动,以证实您可以从SVG获得的一些特性:路径跟踪和自动旋转。这第三个例子证实了我将介绍的第一种类型的声明性动画——animateMotion元素(参见代码3)。

  代码3 移动中的宇宙飞船(只包含更改)

 

<defs>
[...]
<path id="rocket2" transform="rotate(90)" [...]/>
<path id="circle" stroke-width="4" fill="none" stroke="#000000"
d="M100 350 A300,300 0 1,1 700,350 A300,300 0 1,1 100,350"/>
</defs>
[...]
<use xlink:href="#rocket">
<animateMotion rotate="auto" dur="30s" repeatCount="indefinite">
<mpath xlink:href="#circle"/>
</animateMotion>
</use>

 

  在defs元素中,我添加了一个path元素,用于定义火箭移动所沿着的圆圈。我还向火箭定义添加了一个rotate转换,以便火箭在沿着圆圈移动时将面向正确的方向。SVG可以使图像转向,以便它总是面向它所移动方向的切线。我选择了一个圆圈,是因为我的手绘路径使得切线不断变化方向。有些人在写字板上手稳定一些,那么其所得的结果就会好一些。重要的事情是,动画沿着的线路可以是任何任意的路径。还要注意,不是使用简单的circle元素,我使用一个具有椭圆弧形的path创建了圆,所以火箭有了一个具有起点和终点的路径。
  要在您的浏览器中运行这个例子,请下载示例代码(即下载小节中的03_rocket_moving.svg)。如果已经安装好了SVG查看器,则单击查看该动画。

5 UFO观察

  好的,火箭自转幅度在一会儿之后变小了。在第四个例子中,我为画面增加了一个来自未知的UFO中另一世界的访问者(参见图2)。这使我可以证实vanilla animate元素,以及它的兄弟animateColor。

未确定的动态矢量图形

  图2 未确定的动态矢量

  代码4 UFO(只包括更改)

 

<defs>
[...]
<g id="ufo">
<path id="ufo-body" d="M0 50 A100 100
0 0 0 100 50 L100 40 A100 100 0 0 0 0 40 z"/>
<circle id="port-1" class="port" cx="25" cy="45" r="6"/>
<circle id="port-2" class="port" cx="50" cy="45" r="6"/>
<circle id="port-3" class="port" cx="75" cy="45" r="6"/>
</g>
<path id="back-and-forth" d="M100 50 L700 50 z"/>
</defs>
<use xlink:href="#ufo">
<animateMotion dur="50s" repeatCount="indefinite">
<mpath xlink:href="#back-and-forth"/>
</animateMotion>
<animateColor xlink:href="#port-1" dur="10s"
repeatCount="indefinite" attributeName="fill"
values="red;green;blue;red" begin="ufo.load"/>
<animateColor xlink:href="#port-2" dur="10s"
repeatCount="indefinite" attributeName="fill"
values="green;blue;red;green" begin="ufo.load"/>
<animateColor xlink:href="#port-3" dur="10s"
repeatCount="indefinite" attributeName="fill"
values="blue;red;green;blue" begin="ufo.load"/>
</use>

 

 

[...]

 

  在defs中,我添加了一个从简单的SVG弧形和圆构成的ufo,以避免另外一大块来自写字板的path语句。我还添加了一个path作为来回移动的路线 -- -- 一个简单的线条。结束的z结束路径,所以UFO有了返回起点的线路。在代码体中,我有一个比以前更加复杂的use元素,其中包含四个不同的动画元素:一个animateMotion类似于您在火箭那一部分所看到的,然后是三个animateColor块。有趣的是,animateColor元素不直接应用于ufo(这将是默认情况),而是通过使用xlink:href属性,应用于UFO的单独舷窗。我还将每个animateColor的起点绑定到了UFO的主元素的加载。
  要在您的浏览器中运行这个例子,请下载示例代码(即下载一节中的04_ufo.svg)。如果已经安装好了SVG查看器,则单击查看该动画。

6 火箭与UFO

  第五个例子添加用户界面,仍然是通过声明来完成。SVG支持您所期望的所有鼠标事件,但是也支持键盘事件,甚至还支持特定的按键事件,最后一种情况就是我选择将在这里证实的情况。字母表中的每个字母都会触发火箭向前移动(参见图3)。现在该防御您的家庭人造卫星受到UFO的攻击了!

主题为导弹防御的SVG例子

  图3 导弹防御

  对于这个例子,我将从火箭定义中删除transform属性,因为火箭将不再沿着圆圈移动,因而我还删除了圆圈的定义。不再需要对defs作其他更改。所有的动作现在都在代码体中,在主题上有26个变体(参见代码5)。

  代码5 火箭与UFO(只包括更改)

 

<use xlink:href="#rocket" x="4%" y="85%" display="none">
<set id="showa" attributeName="display" to="inherit"
dur="5s" begin="accessKey(a)"/>
<animate dur="5s" begin="showa.begin" attributeName="y"
from="600" to="-100"/>
</use>

 

  每个变体使用我前面定义的相同火箭,但是将它绑定到一个不同的x坐标和一个字母键盘事件。当查看者按一个键时,它会触发set元素,这使得火箭在屏幕底部可见。注意一个普通的animate元素是如何绑定到set事件的启动的。这证实了事件如何可以触发其他事件。如果您想要链接事件,第二个事件可由set事件的结束而不是开始来触发。

  要在您的浏览器中运行这个例子,请下载示例代码(即下载一节中的05_rocket_defense.svg)。如果已经安装好了SVG查看器,则单击查看该动画。记住,您需要按一个或多个字母键来触发火箭运动;根据您的计算机的速度,您可能在火箭出现之前会感觉到一点延迟。

7 流星入侵

  最后一个例子用animateTransform元素结束SVG中声明性动画脚本编程的介绍。这个例子展示了五个火箭,或者说一群火箭,有的出现在页面的中央,有的冲向查看者和消失在边缘(参见图4)。

主题为火箭向你飞来的SVG例子

  图4 火箭向你飞来

  这一效果既需要动画嵌套,又需要动画相互影响。additive="sum"属性允许各种变换的效果相互重叠。第一个内部转换让每个火箭绕着它们自己的轴旋转,所以火箭在靠近查看者时呈纺锤形。第二个内部转换缩放每个火箭使之变大(模拟靠近)。这两个转换都应用于火箭的路径,我用use元素来引入路径。外部转换应用于整个组,将火箭移出视线的边缘和屏幕。
  代码6只展示了一组动画,但是其他的与此类似,只是在屏幕上的位置不同,并且在起始时间和持续时间上错开了,以便出现混合的效果。代码6中的大多数线条数据为了简洁而被截断了。

  代码6 流星(只包括更改)

 

<defs>
[...]
<path id="rock1" d="M-8 -21 L-9 -21 L-15 -21 [...] />
<path id="rock2" d="M19 -15 L18 -17 L14 -20 L12 [...] />
<path id="rock3" d="M-17 -20 L-18 -20 L-19 -20 [...] />
<path id="rock4" d="M11 -14 L11 -15 L10 -15 [...] />
<path id="rock5" d="M14 -30 L13 -30 L12 -29 [...] />
</defs>
[...]
<g>
<use xlink:href="#rock1">
<animateTransform additive="sum" attributeName="transform"
type="rotate" from="0" to="360" dur="6s"
repeatCount="indefinite"/>
<animateTransform additive="sum" attributeName="transform"
type="scale" from="0.1" to="6.0" dur="6s"
repeatCount="indefinite"/>
</use>
<animateTransform additive="sum" attributeName="transform"
type="translate" from="600,400" to="1400,-200" dur="6s"
repeatCount="indefinite"/>
</g>
[...]

 

  要在您的浏览器中运行这个例子,请下载示例代码(即下载一节中的06_asteroids.svg)。如果已经安装好了SVG查看器,则单击查看该动画。

8 局限性

  "简单的事情应该是声明性的。复杂的事情应该是过程性的。"——Adam Bosworth
  SVG中的声明性脚本编程有其自身的局限性。对于冲突检测和实际的键盘导航,您应该添加过程性的JavaScript。存在向SVG 1.2添加约束特性的观点,但是没有被采纳,所以您需要以程序的方式编写约束。添加新的对象也要求脚本编程。与HTML一样,SVG添加方法到DOM接口,用于更高级的用途。当然,您也可以使用通常的XML DOM方法。我认为SVG正在接近它的临界点,在这一点上,SVG的使用将快速增长。在撰写本文时,Firefox的最新beta版还不支持声明性动画,但是Mozilla团队正在为之努力。一旦SVG被构建到浏览器中,效果将是巨大的。生成或查看SVG的新工具在不断涌现。未来比火箭的红色亮光还要光明。

9 下载

  描述:Example code
  名字:x-matters42-examples.zip
  大小:14KB
  下载:http://www.chinasvg.com/images/artimg/0606281007.zip


10 作者简介

  文章作者Dethe Elza的照片
  Dethe Elza,高级技术架构师,Blast Radius
  Dethe Elza喜欢的头衔是Chief Mad Scientist。Dethe的联系方式是 这个 E-mail 地址已经被防止灌水恶意程序保护,您需要激活 Java Script 才能观看 。他在http://livingcode.blogspot.com/处维护着一个主要关于Python和Mac OS X的blog,并为他的孩子们编写程序。欢迎对本专栏提出意见和建议。

  本文作者David Mertz的照片
  David Mertz,作家,Gnosis Software, Inc.
  David Mertz是开放标准的忠实信徒。David的联系方式是 这个 E-mail 地址已经被防止灌水恶意程序保护,您需要激活 Java Script 才能观看 ;在http://gnosis.cx/dW/处可以了解他的生活。欢迎对过去、现在、未来的专栏提出意见和建议。请阅读David编写的《Text Processing in Python》一书。

 

将要更新