[HTML5-SVG]JavaScript 的新領域 – 動態圖片處理(SVG)

來源:互聯網
上載者:User

背景

當 JavaScript 被 Netscape 公司發明出來時,它被用來做一些瑣細的事情,比如校正表單、計算日期、提示使用者;隨著 Web 的迅速發展,這種輕巧而靈活的語言被委以越來越多的任務,動態地修改頁面內容,一致地處理事件,甚至無重新整理地和伺服器互動。然而,與傳統的用戶端編程相比,JavaScript 操作的對象限制在 DOM 模型之內,無法進行圖形編程。所以長久以來,我們在設計網頁時都僅僅是在“搭積木”,而且這些積木只有一種形狀——長方形。這些長方形的積木就是應用在 HTML 元素上的“盒子”模型(box model)。每個盒子有邊框 (border),邊緣(margin)和填充(padding)。我們只能控制這些盒子的大小和有限的樣式。這些方塊的集合對於構建一個傳統的文檔頁面已經足夠了。但是 Web 的流行已經使網頁承擔的任務遠遠超出了傳遞文字資訊。哪裡有流行,哪裡就有需求,哪裡也就有創新。網頁的美工設計已經使靜態頁面的美觀程度絲毫不遜色於傳統的用戶端程式的介面。而創造更加互動的使用者介面更是使在頁面上建立和修改圖片的可能十分吸引人。於是,兩種技術應運而生,使得 JavaScript 的功能擴充到圖形領域。

數字化圖片的兩種方案

在介紹這兩種技術之前,我們先來看看圖片的數字化。將圖片儲存為資料有兩種方案。其一為位元影像,也被稱為光柵圖。即是以自然的光學的眼光將圖片看成在平面上密集排布的點的集合。每個點發出的光有獨立的頻率和強度,反映在視覺上,就是顏色和亮度。這些資訊有不同的編碼方案,在互連網上最常見的就是 RGB。根據需要,編碼後的資訊可以有不同的位 (bit) 數——位深。位元越高,顏色越清晰,對比越高;佔用的空間也越大。另一項決定位元影像的精細度的是其中點的數量。一個位元影像檔案就是所有構成其的點的資料的集合,它的大小自然就等於點數乘以位深。位元影像格式是一個龐大的家族,包括常見的 JPEG/JPG, GIF, TIFF, PNG, BMP。

第二種方案為向量圖。它用抽象的視角看待圖形,記錄其中展示的模式而不是各個點的未經處理資料。它將圖片看成各個“對象”的組合,用曲線記錄對象的輪廓,用某種顏色的模式描述對象內部的圖案(如用梯度描述漸層色)。比如一張留影,被看成各個人物和背景中各種景物的組合。這種更進階的視角,正是人類看世界時在意識裡的反映。向量圖格式有 CGM, SVG, AI (Adobe Illustrator), CDR (CorelDRAW), PDF, SWF, VML 等等。

向量圖中簡單的幾何圖形,只需要幾個特徵數值,就可以確定。比如三角形,只需要確定三個頂點的座標。圓只需要確定圓心的座標和半徑。描述它的函數已知的曲線也只需要幾個參數就能夠確定。如正弦曲線、各種螺線等等。如果用位元影像記錄這些幾何圖案,則需要包含組成線條的各個像素的資料。除了大大節省空間的,向量圖還具有完美的伸縮性。因為記錄的是圖形的特徵,圖形的尺寸任意變化時,都只是做著相似變換,不會出現模糊和失真。相反位元影像的圖片放大到超出原有大小時,各個像素點之間出現空缺,即使用某種演算法填充,也會出現模糊鋸齒等現象,不如向量圖精確。因而向量圖很適合用於記錄諸如符號、表徵圖等簡單的圖形。而位元影像則適合於沒有明顯規律的、顏色豐富細膩的圖片。

兩種技術

現在我們回到 Web 上的畫圖上。對應於圖片數字化的兩種方案,各有一種技術。我們按照它們產生的時間順序來說。這篇文章中,筆者會介紹第一種—— SVG。

SVG

第一種技術來自 XML 家族,叫做 SVG(Scalabe Vetor Graphics) - 可縮放向量圖。作為一種通用的資料格式,XML 自誕生之日起,就不斷表現出表達一切可表達之物的抱負,不僅要接納新出現的各種資訊,還要接收曆史上以其他各種形式儲存的資料。其擴張版圖的雄心,不亞於任何一位野心勃勃的君主。

XML 適合於描述結構化的資料,所以你可能猜到了,如它的名字所示,SVG 選擇的視角是向量圖。實際上,SVG 遠不是第一種用 XML 描述圖片的格式,甚至也不是第一種在 Web 上提出的 XML 與向量圖的組合的標準。在它之前的 1998 年,Macromedia 和 Microsoft 向 W3C 提交了 VML(Vector Markup Language),Adobe 和 Sun 提交了 PGML(Precision Graphics Markup Language),這兩種都是基於 XML 的向量圖規範。隨後,不希望互連網上的向量圖片標準被這些巨頭壟斷的其他公司在 W3C 內成立了一個專門小組 SVG Working Group,在借鑒了前兩種提案後,提出了 SVG 規範,隨後被接納為相當於標準的 W3C 推薦(W3C Recommendation)。以下是迄今為止 SVG 的主要發展曆程:

2001-9SVG 1.0 成為 W3C 推薦。

2003-1SVG 1.1 成為 W3C 推薦。並演化出 SVG Tiny,SVG Basic 和 SVG Full 不同層級的細則。

SVG 1.2 在之後的幾年中一直處於工作草稿(W3C Working Draft)的狀態,現已確定會被 SVG 2.0 取代。

SVG 2.0 將會完全重寫 SVG 1.2,以加入更多諸如 CSS,HTML5 的新特性。

第一個簡單的例子

下面是一個很簡單的向量圖的定義。SVG 中各種元素和屬性的詳細說明可以在專門的參考中找到。本文中會在例子中對一些重要的元素和屬性做說明。

清單 1. 一個 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" width="100%" height="100%" >  <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。你就已經畫完了一幅圖——一個紅紅的太陽。但是想要看它卻不那麼容易。需要用一些專業的繪圖軟體,比如 Adobe Illustrator,CorelDRAW 和 GIMP 才能顯示這個圖片。你的電腦上已經有的 Windows 畫圖、ACDSee 都不支援這種格式。這是可以理解的,因為 SVG 是作為互連網片的一種標準。所以接下來看看怎樣在瀏覽器中顯示它——不幸的是,這仍然不像開啟一幅 JPG 或者 GIF 那麼簡單。

各種瀏覽器對 SVG 的支援不一。總的說來,現在仍舊佔據最大市場份額的 IE 不支援,其他主流瀏覽器,包括現在市場份額第二的 Firefox 以及 Chrome、Safari 和 Opera 都對 SVG 標準有不同程度的支援。IE6、7、8 對 SVG 都沒有原生的支援,需要專門的外掛程式(如 Adobe SVG Viewer)才能顯示。目前還處於技術預覽版的 IE9 將會支援。考慮到 IE 曾經佔據的壟斷性地位和微軟有自身的競爭性的 VML 技術,這種“落後”並不奇怪。

不過這種情況,在發展迅速的瀏覽器市場瞬息萬變。所以最好試試看您使用的瀏覽器支援下列哪種顯示方法。

  1. 使用 <img> 標籤。   

    <img src= 'sun.svg' >
    將 SVG 與傳統的互連網圖片格式同等使用(現在只有 Chrome、Safari 和 Opera 支援)。

  2. 使用 <embed> 標籤。   

    <embed src="sun.svg" width="300" height="100"
        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" width="300" height="100" type="image/svg+xml" codebase="http://www.adobe.com/svg/viewer/install/" /> 
  4. 使用 <iframe> 標籤。   
     <iframe src="sun.svg" width="300" height="100" border="0" style="border-width:0">  </iframe> 

下面是一個測試瀏覽器對 html 中各種使用 SVG 的方式是否支援的頁面代碼。sun.svg 檔案與該頁面儲存於同一目錄。

清單 2. 測試瀏覽器對 SVG 的支援

  <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0 Transitional//EN">  <HTML>  <HEAD>   <TITLE> SVG in HTML </TITLE>  </HEAD>  <BODY>  1. 使用 &lt;img&gt; 標籤 <br>  <img src="sun.svg" width="300" height="100">  <br>  2. 使用 &lt;embed&gt; 標籤 <br>  <embed src="sun.svg" width="300" height="100" type="image/svg+xml" pluginspage="http://www.adobe.com/svg/viewer/install/" />  <br>  3. 使用 &lt;object&gt; 標籤 <br>  <object data="sun.svg" width="300" height="100" type="image/svg+xml" codebase="http://www.adobe.com/svg/viewer/install/" />  <br>  4. 使用 &lt;iframe&gt; 標籤 <br>  <iframe src="sun.svg" width="300" height="100" border="0" style="border-width:0">  </iframe>  </BODY>  </HTML> 

 

動態功能

如果僅僅是將 SVG 作為圖片引用,則只發揮了它的靜態功能。我們更感興趣的是應用它的動態功能。SVG 的動態功能包括兩個方面。一為動畫,二為支援指令碼編程。

動畫

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

清單 3. 用 SIML 實現的動畫

 <?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 檔案,在一個頁面中引用,如果您的瀏覽器支援 SIML,螢幕上會顯示一個不斷旋轉的紅色三角形;如果您的瀏覽器只支援 SVG,將看到一個靜止的紅色三角形。

指令碼可程式化性

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

為了示範這些動態功能,我們採取和上面不同的在頁面中使用 SVG 的方式——在 XHTML(XML 的 XML 版本)直接寫入 SVG 的源文本,而上面的四種方式 SVG 的定義都儲存在和頁面不同的另一個檔案中。這樣做有兩個原因。一是在支援 XHTML 和 SVG 在瀏覽器中,可以通過 JavaScript 直接存取和修改 SVG。二是在互連網的未來標準 HTML 5 中,SVG 就可以這樣直接在 HTML 中定義,就像其他 HTML 元素一樣。

之後的幾個例子都可以在 Firefox 中運行,但無法使用 IE。因為要到版本 9,IE 才會加入對 XHTML 的支援(目前的 IE 只支援將 XHTML 作為 HTML 解釋),再次顯示了擁抱公開標準的遲緩。

我們的第一個例子是一個進度條。在 Firefox 中載入下面的 XHTML 頁面,會顯示一個綠色的運動的進度條。

圖 1. 進度條樣本

清單 4. 進度條代碼

 <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);                     }                 }                 // 每十分之一秒改變一次進度。                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> 

 

對這個 XHTML 需要做一些說明。

<html xmlns="http://www.w3.org/1999/xhtml">
XHTML 的根項目為 html 元素,xmlns 屬性指定 XHTML 的命名空間。

<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 標註。

事件

SVG 中的元素同樣支援使用者介面的事件。因此我們可以通過滑鼠、鍵盤觸發的各種事件改變 SVG 中的圖形。這就使得在整個頁面上可以進行豐富的圖形的互動,而不需要藉助於 Flash 外掛程式。下面通過幾個例子來說明對事件的運用,使用的都是 DOM3 事件規範中定義的方法。

類比控制項

HTML 中的單選鈕、複選框、下拉式清單等標準控制項為使用者輸入和顯示資料提供了各種友好的方式。過去我們只能“使用”它們,現在我們可以類比甚至創造新的控制項。我們先來類比一個簡單的單選鈕的圖形和行為。

圖 2. 類比單選鈕

清單 5. 類比的單選鈕 1 之代碼

 <html xmlns="http://www.w3.org/1999/xhtml">     <head>         <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> 

 

代碼說明:
SVG

這裡共有三個 circle 元素,其中兩個作為空白的選項的圓圈,第三用於標記選中的狀態。circle 中控制大小和位置的幾項屬性我們之前都看過了。stroke-width 屬性用於控製圖形外框線條的寬度。name 屬性是我們自訂的,用於將一組單選鈕關聯在一起。

在 SVG 中嵌入文字需要使用 text 元素。x 和 y 屬性用於控制文字的位置,x 為左端的座標,y 為 下端的座標。

當然,以上只是類比了單選鈕響應滑鼠點擊的外觀,真正的控制項還必須包括讀和寫資料。為此,我們將擴充上面的例子,提供一個可以單獨使用的“單選鈕”。

圖 3. 更完善的類比的單選鈕

實現這樣一個單選鈕的代碼,因為較長,附在參考資源中。代碼中有詳細的注釋。

最後,為了展示 SVG 能創造的生動圖形應用的可能性,這裡給出一個更加複雜和有趣的例子。一個圓、兩個點和一段弧線構成的表情是互連網上著名的符號。這個簡單的圖形不僅可以輕易被看成一張臉,而且通過改變弧線的形狀,還可以表現從笑臉到哭臉的不同表情。它顯示了人腦識別人臉的複雜行為是以模組和模糊的方式進行的,這種抽象的處理方式也為展現 SVG 的功能提供了一個良好的場合。我們先畫出這樣一張高度簡化的臉,再建立一個滑動條控制項,最後用拖動滑動條的方式來控制臉中弧線的彎曲程度和方向。如果把滑塊的高度解釋成象徵一個人的壓力,那麼我們就看到了隨著壓力的改變,人的表情是如何變化的。此代碼較長,可以在參考資源中下載。

圖 4. 一個更複雜的例子

 

 

總結

本文介紹了一種用於互連網的基於 XML 的向量圖技術 SVG 的基本概念,並通過一些執行個體顯示了當與 JavaScript 結合時,這項技術的靈活性和廣泛的應用前景。

參考資源

下載

描述       名字                      大小            下載方法
範例程式碼        sample.zip                       5KB                 HTTP
相關文章

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.