【轉】JavaScript操做SVG的一些知識

原文:http://blog.iderzheng.com/something-about-svg-with-javascript/javascript

前陣子學習了一下SVG(Scalable Vector Graphics),但願能借此彌補本身在圖形藝術上的不足,固然最後也沒有獲得什麼提升,不過也擴充了一些網頁前段技術知識。經過作了一些小的設計項目,也發現SVG能夠彌補一些HTML元素的不足,好比傾斜、弧線、動畫、複用等等。html

雖然SVG和HTML同樣都屬於XML的一種方言,一些基本的JavaScript對HTML的DOM操做都適用於SVG,可是在實際運用中仍是由於這樣那樣的細微區別遇到了不大不小的麻煩。因此經過此篇文章記錄下遇到的問題和解決的方法。
java

獲取SVGDocument

當使用JavaScript在頁面上對HTML進行操做的使用,一個很是重要的對象就是document了。不管是getElementByIdgetElementsByTagName,異或是createElement,它們都是document對象上的方法。並且全部其它任何DOM對象都被包含在該對象以內。瀏覽器

通常而言,一個HTML文件,或者說一個網頁都對應一個document對象,因此若是SVG是直接嵌套在HTML的內容中的話,它們就會共用一個document對象,所以能夠直接經過該對象來獲取到SVG元素對象。app

好比下邊的表明,在瀏覽器上打開,就會看到一個藍色的圈而非綠色的圈,由於JavaScript經過document得到了circle對象,並從新設置了其fill屬性。ide

  1. <html>
  2. <head>
  3. <title>Nested SVG</title>
  4. </head>
  5. <body>
  6.  
  7. <svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink"
  8. version="1.1" width="20" height="20">
  9. <circle id="c" cx="10" cy="10" r="7" fill="green"/>
  10. </svg>
  11. <script type="text/javascript">
  12.  
  13. var c = document.getElementById('c');
  14. c.setAttribute('fill','blue');
  15.  
  16. </script>
  17. </body>
  18. </html>

 

不過大多時候,SVG並不會直接嵌套在HTML之中重現出來,更多的會選擇將其做爲圖片經過img標籤引進,或者當作背景圖片在CSS中經過url引入。對於這種狀況,SVG只是單純的當作圖片來使用並且一個XML類型的文檔,因此也就沒法使用JavaScript來操做它們。svg

還有一類方法則是經過object、embed或者iframe標籤將SVG文件引入到HTML頁面上呈現出來。對於該種狀況, 這些標籤實際上會把經過datasrc屬性指定的內容相對獨立的引入到頁面上來,也就是其中的內容會有徹底屬於本身的document對象。因此使用原來的document對象就沒法取得經過上述標籤引入進來的SVG文檔中元素,更不用說去修改上邊的屬性了。學習

好在當使用JavaScript獲取到這些元素對象的時候,它們都一個方法能夠獲取所引用的SVG文檔的document對象,那就是getSVGDocument()
getSVGDocument動畫

固然這些都是須要瀏覽器支持才行的,對於目前主流最新瀏覽器來講這些都是標配的方法。若是使用object或iframe引入SVG文檔,除了getSVGDocument(),還可使用contentDocument屬性來獲取其引入文檔對應的document對象。區別在於若是是引入的不是SVG文件,而是XML或者HTML等等,contentDocuement依然會返回對象,而getSVGDocument()則返回nullui

contentDocument

獲取了SVG的document以後,就能夠像往常那樣獲取內部元素屬性、綁定事件等等。還能夠定義一個document爲參數的方法造成局部變量,要對某個引入SVG文檔進行操做時就獲取該文檔的document對象傳入,想獲取主文檔的對象時就使用window.document便可。

  1. function setup (document) {
  2. // do something with svg docuemnt
  3. }
  4.  
  5. setup (document.getElementById('svg-embed').getSVGDocument());

 

固然了使用上邊一系列屬性和方法都有一個大前提:要知足同源策略(Same-origin policy)。若引入的SVG文檔是來自於其它站點的,那麼瀏覽器就會禁止獲取document對象。

blocked_accessing

操做SVG的元素

SVG做爲XML的方言,不一樣於HTML鬆散的標籤結構和格式,它嚴格遵循XML的語法格式,因此開始標籤都要有對應的結束標籤,全部標籤都要被svg標籤包含在內。另外在HTML常常被忽略的一個知識就是:XML是有命名空間(namespace)的。命名空間在經過JavaScript建立SVG元素對象的時候就引發了一些麻煩。

通常的在HTML中若想經過JavaScript建立一個元素對象的話,代碼相似以下:

  1. var inp = document.createElement('input');
  2. inp. type = 'button';
  3. inp. value = 'button';
  4. inp. name = 'button';
  5.  
  6. var con = document.getElementById('container');
  7. con. appendChild(inp);

 

可是使用相同的方法,建立SVG元素並添加到SVG文檔中的話, 該元素並不會呈現出來。

  1. <svg xmlns="http://www.w3.org/2000/svg"
  2. xmlns:xlink="http://www.w3.org/1999/xlink"
  3. version="1.1" width="20" height="20">
  4. <script type="text/javascript">
  5. var c = document.createElement('circle');
  6. c.cx = 10;
  7. c.cy = 10;
  8. c.r = 7;
  9. c.fill = 'green';
  10.  
  11. document.rootElement.appendChild(c);
  12. </script>
  13. </svg>

 

這是由於建立SVG元素須要指定命名空間,就像須要在svg標籤上設定xmlns爲http://www.w3.org/2000/svg。正確的構造方式是調用createElentNS()方法,並將」http://www.w3.org/2000/svg」做爲第一參數傳入。

此外,不一樣於HTML元素對象能夠直接對一些屬性賦值,SVG元素對象都須要經過調用setAttribute()方法來設定屬性值。由於大部分屬性都是SVGAnimatedLength類型,即便要經過屬性賦值也要寫成相似c.r.baseVal.value = 7,多層訪問其下屬性。不過像fillstroke等默認都是undefined,因此使用setAttribute()是更好的選擇。

下述代碼就能夠在頁面上呈現是一個半徑爲7px的綠色的圓點。

  1. <svg xmlns="http://www.w3.org/2000/svg"
  2. xmlns:xlink="http://www.w3.org/1999/xlink"
  3. version="1.1" width="20" height="20">
  4. <script type="text/javascript">
  5.  
  6. var c = document.createElementNS('http://www.w3.org/2000/svg','circle');
  7. c. setAttribute('cx', 10);
  8. c. setAttribute('cy', 10);
  9. c. r.baseVal.value = 7;
  10. c. setAttribute('fill', 'green');
  11.  
  12. document. rootElement.appendChild(c);
  13. </script>
  14. </svg>

 

 

 

除了元素有命名空間,有些屬性也有其特定的命名空間。好比在HTML極爲經常使用的a標籤的,在SVG中又有存在,可是對於其href屬性,在SVG之中就必須加上xmlns:前綴來指定其命名空間了。對於設置這些屬性也須要用setAttributeNS()方法並將」http://www.w3.org/1999/xlink「做爲第一參數傳入。

  1. <svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink"
  2. version="1.1" width="20" height="20">
  3. <script type="text/javascript">
  4.  
  5. var a = document.createElementNS('http://www.w3.org/2000/svg','a');
  6. a. setAttributeNS('http://www.w3.org/1999/xlink',
  7. 'xlink:href',
  8. 'http://blog.iderzheng.com/');
  9.  
  10. var c = document.createElementNS('http://www.w3.org/2000/svg','circle');
  11. c. setAttribute('cx', 10);
  12. c. setAttribute('cy', 10);
  13. c. r.baseVal.value = 7;
  14. c. setAttribute('fill', 'green');
  15.  
  16. a. appendChild(c);
  17.  
  18. document. rootElement.appendChild(a);
  19. </script>
  20. </svg>

 

 

 

 

如今能夠經過點擊綠點進入到博客主頁了。

不只是a標籤,對於其它標籤,例如:use、image,若要設置xlink:href屬性時都應使用命名空間的方式,不然就不會起做用。對於其它該命名空間下的屬性也是如此,例如xlink:titlexlink:show。這裏就不一一列舉,具體關於xlink的可參看此處

若是你碰到了其它在JavaScript中因XML命名空間引發的問題,也歡迎留言補充。

SVG與窗口座標系的轉換

SVG比HTML的一大優點在於前者支持平移、縮放、切變等變換(Transform)。雖然如今CSS3也支持這些變換,可是畢竟SVG是向量圖,它在縮放的時候不會丟失像素。並且SVG能夠經過use標籤設置tranform屬性來進行快速複用。而HTML的標籤就沒有這樣的「複用性」,每一個在頁面上呈現出來的內容都嚴格對應一個標籤元素。

不只是transform屬性能夠變化座標,一些元素例如svg,也能夠經過設定viewBox來改變自身的座標系以影響呈現的內容。這就引起了一些問題座標系轉換的問題:好比鼠標點擊在頁面上的時候獲取到的是基於窗口座標系以像素爲單位的位置,如何轉變到SVG的座標系的點以肯定被點擊的對象或者進行其它操做呢?

一種方法能夠是本身記錄下窗口和SVG圖的比例,而後根據比例進行轉換。這可能能夠必定程度地解決平移和縮放帶來的變換問題,可是對於旋轉就無能爲力了。並且當窗口進行了滾動或者拖拽,都須要從新計算這些比例。

其實在每一個SVG元素對象上,都有一個getScreenCTM()的方法,它會返回一個SVGMatrix來表示元素的座標系所作過的變換。此外SVG還有一種SVGPoint類型,它有x和y兩個屬性能夠表示任一一個點,同時它還有一個matrixTransform()方法能夠將點跟某個SVGMatrix相乘獲得相應矩陣變換後的點。經過這些再加上一些線性代數的知識,就能夠輕鬆的進行座標系的變換了。

要注意的是SVGPoint只能經過svg元素對象的createSVGPoint()來建立,不能用new SVGPoint()這樣的方式。

  1. function click(e) {
  2. // rootElement is specific to SVG document
  3. // documentElemnt is to any XML document include HTML
  4. // they both can retrieve the root element of a document
  5. var r = document.rootElement || document.documentElement,
  6. pt = r.createSVGPoint(),
  7. im = r.getScreenCTM().inverse(); // inverse of tranforma matrix
  8.  
  9. // set point with window coordination
  10. pt. x = e.clientX;
  11. pt. y = e.clientY;
  12.  
  13. // convert point to SVG coordination
  14. var p = pt.matrixTransform(im);{
  15. }
相關文章
相關標籤/搜索