讀古今文學網 > 編寫高質量代碼:改善JavaScript程序的188個建議 > 建議184:使用SVG創建動態圖形 >

建議184:使用SVG創建動態圖形

與傳統的客戶端編程相比,JavaScript操作的對象被限制在DOM模型之內,無法進行圖形編程。所以長久以來,在設計網頁時都僅僅是在「搭積木」,並且這些積木只有一種形狀——長方形。這些「長方形的積木」就是應用在HTML元素上的「盒子」模型(box model)。每個盒子有邊框(border)、邊緣(margin)和填充(padding)。我們只能控制這些盒子的大小和有限的樣式。這些方塊的集合對於構建一個傳統的文檔頁面已經足夠了,但Web的流行已經使網頁承擔的任務遠遠超出了傳遞文字信息。

SVG(Scalable Vetor Graphics,可縮放矢量圖)作為一種通用的數據格式,屬於XML語言的一個分支,主要負責描述矢量圖的數據結構關係。實際上,SVG不是第一種用XML描述圖片的格式,甚至也不是第一種在Web上提出的XML與矢量圖的組合的標準。在它之前的VML(Vector Markup Language)和PGML(Precision Graphics Markup Language)都是基於XML的矢量圖規範。

SVG中各種元素和屬性的詳細說明可以參考相關規範文檔。下面代碼是一個簡單的SVG文件。


<?xml version=\"1.0\"standalone=\"no\"?>

<!DOCTYPE svg PUBLIC\"-//W3C//DTD SVG 1.1//EN\"\"http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd\">

<svg xmlns=\"http://www.w3.org/2000/svg\"version=\"1.1\">

<circle cx=\"100\"cy=\"100\"r=\"40\"fill=\"red\"/>

</svg>


第一條語句為XML指令定義版本,並說明此文件引用到其他文件。第二條語句是文檔類型定義,規定此XML中哪些是有效的SVG元素。這裡引用的http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd正是第一條語句中standalone屬性為no的原因。從第三條語句開始是SVG的真正定義,circle元素指定畫一個圓,cx、cy和r屬性分別指定圓心的橫坐標、縱坐標和半徑,fill屬性指定用紅色填充此圓內部的區域。

將這段代碼粘貼進任何一個文本編輯器,然後將文件保存為一個SVG文件,如sun.svg,就可以畫出一個紅色的太陽。各種瀏覽器對SVG的支持情況不同。總的來說,IE 6、IE 7和IE 8對SVG都沒有原生的支持,需要專門的插件(如Adobe SVG Viewer)才能顯示,IE 9支持SVG,不過IE有自定義的VML技術可以實現相同的效果。其他主流瀏覽器都對SVG標準有不同程度的支持。

(1)使用<img>標籤顯示


<img src=\'sun.svg\'>


將SVG與傳統的互聯網圖片格式同等使用(現在只有Chrome、Safari和Opera支持)。

(2)使用<embed>標籤顯示


<embed src=\"sun.svg\"

type=\"image/svg+xml\"

pluginspage=\"http://www.adobe.com/svg/viewer/install/\"/>


pluginspage屬性的值是Adobe公司為不原生支持SVG的瀏覽器開發的插件Adobe SVG Viewer的安裝地址。2009年1月1日,Adobe公司已經終止對該產品的支持。

(3)使用<object>標籤顯示


<object data=\"sun.svg\"

type=\"image/svg+xml\"

codebase=\"http://www.adobe.com/svg/viewer/install/\"/>


(4)使用<iframe>標籤顯示


<iframe src=\"sun.svg\"border=\"0\">

</iframe>


下面是對在HTML中各種使用SVG的方式是否支持進行判斷的頁面代碼,sun.svg文件與該頁面保存於同一目錄。


<!DOCTYPE HTML PUBLIC\"-//W3C//DTD HTML 4.0 Transitional//EN\">

<HTML>

<HEAD>

<TITLE>SVG in HTML</TITLE>

</HEAD>

<BODY>

1.使用<img>標籤

<br>

<img src=\"sun.svg\">

<br>

2.使用<embed>標籤

<br>

<embed src=\"sun.svg\"

type=\"image/svg+xml\"

pluginspage=\"http://www.adobe.com/svg/viewer/install/\"/>

<br>

3.使用<object>標籤

<br>

<object data=\"sun.svg\"

type=\"image/svg+xml\"

codebase=\"http://www.adobe.com/svg/viewer/install/\"/>

<br>

4.使用<iframe>標籤

<br>

<iframe src=\"sun.svg\"border=\"0\">

</iframe>

</BODY>

</HTML>


如果僅將SVG作為圖片引用,則只發揮了它的靜態功能。SVG的動態功能包括兩個方面:支持動畫和支持腳本編程。

SVG在設計時就加入了對動畫的支持,這是通過另一種W3C頒布的動畫語言SMIL(Synchronized Multimedia Integration Language)實現的。SMIL在應用時與SVG結合得非常緊密,它與SVG一樣,是一種聲明性(declarative)的標記語言,通過元素(element)和屬性(attribute)來定義動畫的行為。這裡只給出一個簡單的例子,不做詳細介紹,因為瀏覽器對它的支持還很有限,另外SMIL聲明性的本質也使其表現力受到限制,不如使用腳本自定義動畫靈活。

(5)用SMIL實現的動畫


<?xml version=\"1.0\"standalone=\"no\"?>

<!DOCTYPE svg PUBLIC\"-//W3C//DTD SVG 1.1//EN\"

\"http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd\">

<svg xmlns=\"http://www.w3.org/2000/svg\">

<polygon points=\"50,100 100,100 75,50\"stroke=\"#660000\"fill=\"#cc3333\">

<animateTransform

attributeName=\"transform\"

begin=\"0s\"

dur=\"10s\"

type=\"rotate\"

from=\"0 0 0\"

to=\"360 60 60\"

repeatCount=\"indefinite\"

/>

</polygon>

</svg>


polygon元素指定畫一個多邊形,由於這裡給定了三個頂點,所以是一個三角形。將上面的代碼保存成一個SVG文件,在一個頁面中引用該文件,如果此時的瀏覽器支持SMIL,屏幕上會顯示一個不斷旋轉的紅色三角形;如果此時的瀏覽器只支持SVG,將看到一個靜止的紅色三角形。

(6)腳本可編程性

SVG是一個XML文件,用於XML編程的兩種模型DOM和SAX也適用於它。因為SVG是被設計用於互聯網的,所以通過JavaScript和DOM訪問它就是最重要的應用模式。我們已經熟悉了通過JavaScript和DOM動態地修改HTML,同樣也可以在瀏覽器中動態地創建、修改和刪除圖片,這也是接下來要介紹的在SVG方面的重點。

為了演示這些動態功能,可以採取和前面的在頁面中使用SVG不同的方式——在XHTML中直接寫入SVG的源文本,而在前面的4種方式中,SVG的定義都保存在和頁面不同的另一個文件中。這樣做有兩個原因:一是在支持XHTML和SVG的瀏覽器中,可以通過JavaScript直接訪問和修改SVG;二是在互聯網的未來標準HTML 5中,SVG可以這樣直接在HTML中定義,就像其他HTML元素一樣。

下面設計一個進度條,顯示一個綠色的運動的進度條。


<html xmlns=\"http://www.w3.org/1999/xhtml\">

<head>

<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);

}

}

//每0.1秒改變一次進度

var id=setInterval(test,100);

}

//頁面載入後開始測試

window.addEventListener(\'load\',testBar,true);

/*]]>*/

</script>

</head>

<body>

<p id=\'svgDiv\'>

<svg xmlns=\"http://www.w3.org/2000/svg\"version=\"1.1\"

viewBox=\"0 0 100 100\">

<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>

</p>

</body>

</html>


<svg xmlns=\"http://www.w3.org/2000/svg\"version=\"1.1\"viewBox=\"0 0 100 100\">表示在XHTML中直接插入svg元素,並且指定命名空間等其他屬性。

viewBox定義矢量圖可見的坐標空間,4個數字依次是原點的x坐標、y坐標以及平面的寬度、高度。SVG的坐標空間符合計算機中指定屏幕空間的慣例,x坐標軸的正方向向右,y坐標軸的正方向向下。

style屬性指定svg元素的各種外觀特性。SVG與HTML一樣,可以應用CSS定義外觀,並且有一些專門的特性——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引擎識別。

<svg>標籤內有一個<g>標籤和兩個<rect>標籤。g元素用於分組。分組不僅可以使SVG的內容結構清晰,還可以使同一組內的對象被集體操作。rect元素代表一個矩形,x、y、width和height屬性分別指定矩形左上頂點的橫坐標、縱坐標,以及矩形的寬度和長度;stroke屬性指定圖形外框的線條顏色。我們用第一個空心的矩形顯示進度條的外框,第二個實心的綠色矩形顯示變化的進度。為了在腳本中方便地訪問,我們設置了綠色矩形的id屬性。

在JavaScript腳本中用DOM先後獲得綠色矩形對象並修改它的寬度屬性。getElementById和setAttribute的用法和在HTML中沒有兩樣。注意,有些在操作HTML時使用的方法,在XML中是不存在的,如根據名稱獲取元素的getElementsByName。

在這個例子中,前三點特別設定有些麻煩,不過這些在正在獲得越來越多支持並且很快將成為互聯網的現實標準的HTML 5中都不是必須的。在HTML 5中,不需要在html和svg元素中指定命名空間,svg和其中的各種標籤會被自動識別,JavaScript代碼也會和在目前的HTML頁面中一樣,不需要在兩側加上CDATA標注。

SVG中的元素同樣支持用戶界面的事件,因此可以通過鼠標和鍵盤觸發的各種事件改變SVG中的圖形,使得在整個頁面上可以進行豐富的圖形互動,而不需要借助於Flash插件。