級(jí)別: 初級(jí)
David Mertz (mertz@gnosis.cx), Drawn this way, Gnosis Software, Inc.
2005 年 5 月 16 日
可縮放矢量圖形(Scalable Vector Graphics,SVG)是一種用于描述與比例無關(guān)的圖形的 XML 格式,可以很好地支持免費(fèi)軟件和商業(yè)工具。在本期文章中,David 將介紹使用 SVG 編寫腳本和動(dòng)畫,還將涉及通過 DOM 處理 SVG 等內(nèi)容。SVG 由于是 XML 格式,因此可以支持通常用 XML 工具和庫進(jìn)行的轉(zhuǎn)換和生成。
在 SVG 于 2001 年左右首次出現(xiàn)以前,已有相當(dāng)多功能強(qiáng)大的矢量圖形格式。Postscript 及其類似產(chǎn)品 PDF 廣泛應(yīng)用于許多應(yīng)用程序。其他特定于應(yīng)用程序的格式包括基于 Postscript 的 Adobe Illustrator (.ai)、CorelDRAW (.cdr)、Computer Graphics Metafile (.cgm)、Windows Metafile (.wmf)、Autocad (.dxf)、Hewlett-Packard Graphics Language (.hpgl)、WordPerfect (.wpg) 等。對(duì)于甚至可以組合動(dòng)畫、聲音和交互性的矢量繪圖,則常使用 Macromedia 的 SWF/Flash 在萬維網(wǎng)上發(fā)布相關(guān)內(nèi)容。
SVG 與所有其他格式的主要區(qū)別在于,前者是 XML 的一個(gè)應(yīng)用程序。盡管對(duì)于同等的圖形,使用 SVG 描述比使用多數(shù)其他矢量格式描述要復(fù)雜得多,但對(duì)于編程操作而言,SVG 則是一種更通用的工具。尤其是,您可以在使用 ECMAScript 和其他文檔對(duì)象模型 (DOM) 的 Web 瀏覽器(或其他應(yīng)用程序)中操縱 SVG。而且同樣重要的是,您可以使用熟悉的 XML 技術(shù)(如 XSLT)或 XML 支持庫轉(zhuǎn)換和生成 SVG。可以將 SVG 與其他使用名稱空間的 XML 格式混合。而且,甚至可以使用層疊樣式表 (CSS) 限定 SVG 的樣式??傊?,在 XML 和 Web 空間,SVG 是一個(gè)友好的播放器。
除了是 XML 格式以外,SVG 還是一個(gè)由 W3C 發(fā)布的完全開放的標(biāo)準(zhǔn)(請(qǐng)參閱 參考資料)。與上述多數(shù)矢量格式不同,SVG 是完全免費(fèi)的,沒有任何版權(quán)或?qū)@麢?quán)限制,而且其規(guī)范完全 有文檔記錄。與其他 W3C 標(biāo)準(zhǔn)類似,規(guī)范文檔本身是 受版權(quán)保護(hù)的 —— 但根據(jù) W3C 的非限制性條款的規(guī)定,允許廣泛傳播以及免費(fèi)復(fù)制和使用(例如,規(guī)范中沒有附帶禁止公開協(xié)議)。
入門
令人高興的是,您可以在多數(shù)新式 Web 瀏覽器中查看 SVG,可以在本機(jī)也可以通過插件查看。精確的支持狀態(tài)在不斷變動(dòng),但通過正確的技術(shù),您應(yīng)能夠使用 Firefox/Mozilla、KHTML(Konqueror 和 Safari)、Opera 或 Amaya 查看 SVG。使用 Adobe 或 Corel 的插件,甚至還可以讓 Internet Explorer 顯示 SVG。還存在一些獨(dú)立的 SVG 查看器,特別是使用免費(fèi)軟件 Batik SVG 工具包(是 Apache XML 項(xiàng)目的一部分 —— 請(qǐng)參閱“參考資料”)。
在許多情況下,SVG 文件是作為獨(dú)立的文檔查看的。在這樣的情況下,這些文件表現(xiàn)為 MIME 類型 image/svg+xml ,并通常有一個(gè)文件擴(kuò)展名 .svg。Gzip 壓縮的 SVG 文件應(yīng)有擴(kuò)展名 .svgz,可由多數(shù)啟用 SVG 的工具直接支持。SVG 文件只是具有適當(dāng) DTD 的 XML 文件。您將在下面幾個(gè)例子中了解到這一點(diǎn)。
然而,可能更為常見的是,您可以將一個(gè) SVG 文檔嵌入較大的文檔中,特別是嵌入 XHTML 頁。其他復(fù)合 XML 格式(如 OASIS OpenDocument)也可以(或?qū)⒅С郑?SVG 嵌入其中。將 SVG 圖形嵌入 (X)HTML 頁有三種方式:
- 通過
<object> 標(biāo)簽
- 通過
<embed> 標(biāo)簽
- 作為嵌入的名稱空間
不過,具體使用哪一種方式有效取決于您的瀏覽器和版本。例如,我創(chuàng)建了下面的 XHTML 文檔(使用 doctype 來支持名稱空間嵌入):
清單 1. XHTML 文檔 (svg-nested.html)
<?xml version="1.0" standalone="no"?>
<!DOCTYPE html PUBLIC
"-//W3C//DTD XHTML 1.1 plus MathML 2.0 plus SVG 1.1//EN"
"http://www./2002/04/xhtml-math-svg/xhtml-math-svg.dtd">
<html xmlns="http://www./1999/xhtml" xml:lang="en">
<head>
<title>SVG as embedded object and nested namespace</title>
</head>
<body>
<h2>Object tag</h2>
<object type="image/svg+xml" data="standalone.svg">
Your browser is currently unable to display SVG images.
</object>
<h2>Nested namespace</h2>
<svg:svg version="1.1" width="5cm" height="4cm"
xmlns:svg="http://www./2000/svg">
<svg:title>Four rectangles</svg:title>
<svg:rect x="0.5cm" y="0.5cm" width="2cm" height="1cm"/>
<svg:rect x="0.5cm" y="2cm" width="1cm" height="1.5cm"/>
<svg:rect x="3cm" y="0.5cm" width="1.5cm" height="2cm"/>
<svg:rect x="3.5cm" y="3cm" width="1cm" height="0.5cm"/>
<!-- Show outline of canvas using 'rect' element -->
<svg:rect x=".01cm" y=".01cm" width="4.98cm" height="3.98cm"
fill="none" stroke="blue" stroke-width=".02cm" />
</svg:svg>
<h2>Embed tag</h2>
<embed id="svg3" src="standalone.svg" />
</body>
</html>
|
通過嘗試,Safari/KHTML 在瀏覽器中表現(xiàn)最好。盡管如此,如果文件使用 .html 擴(kuò)展名,而不是 .xml 擴(kuò)展名命名,結(jié)果會(huì)更好。總之,<embed> 也許是最成功的方法。您可能會(huì)看到文檔呈現(xiàn)為:
圖 1. Web 瀏覽器顯示的 svg-nested.html
另外,本文提供的示例是組合基本形狀、文本、顏色等內(nèi)容的較為簡(jiǎn)單的示例 —— 但 SVG 完全能夠表現(xiàn)復(fù)雜和引人入勝的繪圖。為了提高讀者的興趣,下面是 Ghostscript 和其他工具中包括的著名的 PostScript 老虎圖片,它使用 SVG 呈現(xiàn)(我僅調(diào)整了它的總體大?。?/p>
圖 2. Web 瀏覽器將老虎圖像顯示為 SVG
SVG 文檔的特點(diǎn)
前面的 XHTML 例子(參閱 清單 1)為您顯示了非?;镜?SVG 繪圖。引用的外部文件 (standalone.svg) 所包含的元素與 XHTML 中嵌入的相同,只是去掉了標(biāo)簽中額外的名稱空間限定符。SVG 為您提供了許多圖形元件,而且每個(gè)圖元都有可以進(jìn)一步指定圖形的多種 XML 屬性:顏色、大小、位置、填充、輪廓等。不過,直觀的圖形元件(如橢圓、矩形或多邊形)或者一些可能包括立方體或二次貝塞爾曲線等的更為復(fù)雜的 <path> 元素,常常包括在 <g> 元素中,以便將若干個(gè)圖元組合在一起。<g> 組的優(yōu)點(diǎn)是,您可以對(duì)它進(jìn)行整體縮放、移動(dòng)、設(shè)定樣式或修改。 對(duì)組的修改通用于其中的形狀集合(包括嵌入的 <g> 組)。在編寫 SVG 文檔腳本時(shí)這特別有用。
始終如一
有關(guān) SVG 文檔需要注意的一點(diǎn)是,它們并非完全 是真正的 XML。從語句上說,SVG 確實(shí)是 XML,但在 SVG 屬性中,SVG 繪圖信息內(nèi)容的重要部分包含在逗號(hào)和空格分隔的數(shù)據(jù)中。這里所說的信息內(nèi)容 并不是指 XML Infoset,僅指它包含什么?的更加非正式的概念。這樣做是一種合理的折衷方法,因?yàn)閷?duì)定義曲線的每個(gè)點(diǎn)或句柄都使用子元素將會(huì)使 SVG 更加繁瑣。但是,XML 級(jí)處理技術(shù)(如 XSLT)因此能夠不用真正處理太多的路徑數(shù)據(jù)。例如,下面是一個(gè)二次貝塞爾曲線路徑元素:
<path d="M200,300 Q400,50 600,300 T1000,300"
fill="none" stroke="red" stroke-width="5" />
|
下面是一個(gè)描述五角星形的多邊形:
<polygon fill="red" stroke="blue" stroke-width="10"
points="350,75 379,161 469,161 397,215
423,301 350,250 277,301 303,215
231,161 321,161" />
|
添加樣式
前面提到過您可以使用 CSS 選擇器和語法來修改 SVG 繪圖的外觀。對(duì)于 HTML 和其他支持 CSS 的格式,既可以指定內(nèi)嵌 CSS 信息,也可以作為對(duì)外部樣式表的引用。下面是一個(gè)非常簡(jiǎn)單的內(nèi)嵌 CSS 的例子:
清單 2.一個(gè) 簡(jiǎn)單的 CSS 例子 (inline-styled.svg)
<?xml version="1.0" standalone="no"?>
<!-- By ref:
<?xml-stylesheet href="mystyle.css" type="text/css"?>
-->
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN"
"http://www./Graphics/SVG/1.1/DTD/svg11.dtd">
<svg width="10cm" height="5cm" viewBox="0 0 1000 500"
xmlns="http://www./2000/svg" version="1.1">
<defs>
<style type="text/css"><![CDATA[
rect {
fill: red;
stroke: blue;
stroke-width: 3
} ]]>
</style>
</defs>
<rect x="200" y="100" width="600" height="300"/>
</svg>
|
盡管使用 CSS 無疑可以限定整個(gè)標(biāo)簽的樣式,但您可能會(huì)發(fā)現(xiàn),對(duì) CSS 和 SVG 使用類選擇器會(huì)更好。例如,您可以在樣式表中定義各種類型的矩形,然后將一個(gè) class XML 類屬性附加到繪圖中的每個(gè)矩形,而不用重復(fù)使用為類定義的整個(gè)顏色、填充、描邊和其他屬性列表。只須更改樣式表,就可以更改圖表的整體外觀,使之更適用于不同的環(huán)境。
重復(fù)使用元素
除使用 CSS 之外,清單 2 顯示了 SVG 另一種很好的功能:您可以在 SVG 文檔中包括預(yù)定義的內(nèi)容 —— 在呈現(xiàn)的文檔內(nèi)或文檔之外定義的內(nèi)容。
使用預(yù)定義內(nèi)容作為 SVG 繪圖一部分的方法之一是使用 <image> 元素。在概念上,SVG 中的 <image> 與 HTML 中的 <img> 非常類似:該元素只須指示呈現(xiàn)客戶機(jī)在當(dāng)前的 SVG 環(huán)境中繪制外部圖像的內(nèi)容 —— 其本身可能是 SVG 或者是 JPEG 或 PNG 格式的光柵圖像。您幾乎可以像調(diào)整一個(gè)規(guī)則的圖形元素那樣來調(diào)整外部圖像的大小和改變其位置。例如:
清單 3. 在當(dāng)前圖像中包括一個(gè)外部 SVG 繪圖
<image x="200" y="200" width="100px" height="100px"
xlink:href="http:///external.svg">
|
比 <image> 標(biāo)簽更有意思的也許是令人叫好的 <defs> 和 <use> 元素。首先,您可以使用 CSS 例子中所看到的內(nèi)容 創(chuàng)建 SVG 元素,這些元素在定義時(shí)沒有直接呈現(xiàn) —— 通常,SVG 呈現(xiàn)模型嚴(yán)格按照 SVG 文檔中出現(xiàn)的順序繪制每個(gè)對(duì)象,每個(gè)對(duì)象均覆蓋最后一個(gè)。但 <style> 不是典型的樣式,以后不能真正呈現(xiàn)其本身。
您可以在諸如 <defs> 節(jié)中包括想要包括的任何圖形元素,其中包括 <g> 組和 <symbol> 元素(符號(hào)與組類似;由于本文篇幅所限,這里不對(duì)其區(qū)別進(jìn)行說明)。在定義的外部,您可以使用在 <defs> 節(jié)中定義的圖形元素 —— 甚至可使用在外部 SVG 文檔的 <defs> 節(jié)中定義的圖形元素。例如:
清單 4. 使用預(yù)定義的圖形元素
<?xml version="1.0" standalone="no"?>
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN"
"http://www./Graphics/SVG/1.1/DTD/svg11.dtd">
<svg width="7cm" height="3cm" viewBox="0 0 70 30" version="1.1"
xmlns="http://www./2000/svg"
xmlns:xlink="http://www./1999/xlink">
<desc>'use' with a 'transform' attribute</desc>
<defs>
<rect id="MyRect" x="0" y="0" width="40" height="5"/>
</defs>
<use xlink:href="#MyRect" transform="translate(10,23) rotate(-30)" />
<use xlink:href="#MyRect" transform="translate(20,3) rotate(30)" />
<use xlink:href="http:///foo.svg#OtherRect"/>
</svg>
|
在 清單 4 中,定義相同的矩形使用兩種不同的轉(zhuǎn)換呈現(xiàn),然后還呈現(xiàn)了外部定義的元素。(id 名稱表明它也是一個(gè)矩形,但從該片斷中不能明顯確定 —— 事實(shí)上,外部?jī)?nèi)容可以在呈現(xiàn)之間更改)。
動(dòng)畫和腳本
剛才已提到,使用 ECMAScript 可以編寫 SVG 腳本。原則上,這可讓 SVG 文檔與用戶操作交互。為了支持 Web 應(yīng)用程序空間,SVG 還包含一個(gè)用于超鏈接的與 HTML 類似的 <a> 元素。根據(jù)鼠標(biāo)在特定的圖形元素上單擊,SVG 中的簡(jiǎn)單交互可以修改文檔。清單 5 中的例子雖然很普通,但您可以方便地讓 SVG 圖形響應(yīng)。例如,在下面的流程圖中的圖形或?qū)ο髤^(qū)域上單擊:
清單 5. 讓 SVG 形狀響應(yīng)單擊
<svg>
<title>ECMAScript function for an onclick event</title>
<desc>Simplified from
http://www./TR/SVG11/images/script/script01.svg</desc>
<script type="text/ecmascript"> <![CDATA[
function circle_click(evt) {
var circle = evt.target;
var currentRadius = circle.getAttribute("r");
if (currentRadius == 100)
circle.setAttribute("r", currentRadius*2);
else
circle.setAttribute("r", currentRadius*0.5);
} ]]>
</script>
<!-- Act on each click event -->
<circle onclick="circle_click(evt)" cx="300" cy="225" r="100" fill="red"/>
</svg>
|
您還可以使用 ECMAScript 和 DOM 讓 SVG 圖形產(chǎn)生動(dòng)畫效果。例如,清單 6 中的代碼可以使文本產(chǎn)生一個(gè)好看的效果,即增加和更改不透明度:
清單 6. 使用 JavaScript 讓 SVG 產(chǎn)生動(dòng)畫效果
<svg viewBox="0 0 400 200"
onload="StartAnimation(evt)" version="1.1"
xmlns="http://www./2000/svg">
<script type="text/ecmascript"><![CDATA[
var txt, step=0;
function StartAnimation(evt) {
txt = evt.target.ownerDocument.getElementById("Text");
ShowAndGrowElement();
}
function ShowAndGrowElement() {
step = step+1;
if (step > 200) return;
// Scale text string gradually until it is 20 times larger
txt.setAttribute("transform", "scale("+ step/10 +")" );
// Make the string more opaque
txt.setAttribute("opacity", step/200);
// Call ShowAndGrowElement again 50 milliseconds later.
setTimeout("ShowAndGrowElement()", 50)
}
window.ShowAndGrowElement = ShowAndGrowElement
]]></script>
<g transform="translate(50,150)" fill="red" font-size="7">
<text id="Text">SVG</text>
</g>
</svg>
|
純粹的 SVG 腳本
使用 ECMAScript 可為您提供完美的編程靈活性,但如果您所需要的只是動(dòng)畫,SVG 還提供了 <animate> 和相關(guān)的標(biāo)簽(如 <animateMotion> 或 <animateColor> )。它們都非常靈活,可讓您以各種方式分別賦予 SVG 文檔中每個(gè)元素動(dòng)畫效果。例如,清單 6 中的代碼可產(chǎn)生與 清單 5 中所示技術(shù)相同的增加和呈現(xiàn)不透明效果:
清單 6. 單獨(dú)使用 SVG 表現(xiàn)動(dòng)畫效果
<svg viewBox="0 0 400 200" xmlns="http://www./2000/svg">
<g transform="translate(50,150)" fill="red" font-size="7">
<text id="Text">SVG
<animateTransform attributeName="transform" attributeType="XML"
type="scale" from="0" to="20" begin="0s" dur="10s"
fill="freeze" />
<animate attributeName="opacity" attributeType="CSS"
from="0" to="1" begin="0s" dur="10s" repeatCount="1" />
</text>
</g>
</svg>
|
結(jié)束語
本文初步探討了 SVG 格式的一些基本知識(shí)。撰寫本文讓我對(duì) SVG 作為一種格式而感到興奮不已。Web 確實(shí)需要矢量格式以與比例無關(guān)的方式有效地傳達(dá)復(fù)雜圖形。再加上腳本、動(dòng)畫、鏈接和所有其他內(nèi)容使 SVG 更加有用。所幸的是,多數(shù) Web 瀏覽器現(xiàn)在能夠很好地支持 SVG,因此部署基于 SVG 的圖形和簡(jiǎn)單的 Web 應(yīng)用程序?qū)嶋H上并不存在什么障礙。
|