【转】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. }
相关文章
相关标签/搜索