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

前阵子学习了一下SVG(Scalable Vector Graphics),希望能借此弥补自己在图形艺术上的不足,当然最后也没有得到什么提高,不过也扩充了一些网页前段技术知识。通过做了一些小的设计项目,也发现SVG可以弥补一些HTML元素的不足,比如倾斜、弧线、动画、复用等等。

虽然SVG和HTML一样都属于XML的一种方言,一些基本的JavaScript对HTML的DOM操作都适用于SVG,但是在实际运用中还是因为这样那样的细微区别遇到了不大不小的麻烦。所以通过此篇文章记录下遇到的问题和解决的方法。

获取SVGDocument

当使用JavaScript在页面上对HTML进行操作的使用,一个非常重要的对象就是document了。无论是getElementByIdgetElementsByTagName,异或是createElement,它们都是document对象上的方法。而且所有其它任何DOM对象都被包含在该对象之内。

一般而言,一个HTML文件,或者说一个网页都对应一个document对象,所以如果SVG是直接嵌套在HTML的内容中的话,它们就会共用一个document对象,因此可以直接通过该对象来获取到SVG元素对象。

比如下边的代表,在浏览器上打开,就会看到一个蓝色的圈而非绿色的圈,因为JavaScript通过document获得了circle对象,并重新设置了其fill属性。

  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来操作它们。

还有一类方法则是通过object、embed或者iframe标签将SVG文件引入到HTML页面上呈现出来。对于该种情况, 这些标签实际上会把通过datasrc属性指定的内容相对独立的引入到页面上来,也就是其中的内容会有完全属于自己的document对象。所以使用原来的document对象就无法取得通过上述标签引入进来的SVG文档中元素,更不用说去修改上边的属性了。

好在当使用JavaScript获取到这些元素对象的时候,它们都一个方法可以获取所引用的SVG文档的document对象,那就是getSVGDocument()

当然这些都是需要浏览器支持才行的,对于目前主流最新浏览器来说这些都是标配的方法。如果使用object或iframe引入SVG文档,除了getSVGDocument(),还可以使用contentDocument属性来获取其引入文档对应的document对象。区别在于如果是引入的不是SVG文件,而是XML或者HTML等等,contentDocuement依然会返回对象,而getSVGDocument()则返回null

获取了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对象。

操作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. }

【转】JavaScript操作SVG的一些知识的更多相关文章

  1. JavaScript对SVG进行操作的相关技术

    原文地址:http://www.ibm.com/developerworks/cn/xml/x-svgscript/   本文主要介绍在 SVG 中通过编程实现动态操作 SVG 图像的知识. SVG ...

  2. 原创 HTML5:JS操作SVG实践体会

    在工业信息化系统里,常常需要动态呈现系统的数据在一张示意图里,用于展现系统状态,分析结果等.这样用JavaScript操作svg 元素就有现实意义.本人近期做了一些实践,现分享一下. 需求: 你下面这 ...

  3. Cookie介绍及JavaScript操作Cookie方法详解

    本文主要为大家简单介绍了以下Cookie的用途.运行机制,以及JavaScript操作Cookie的各种方法,总结的比较全面,希望能给大家带来帮助. 什么是 Cookie “cookie 是存储于访问 ...

  4. JavaScript 操作 DOM 常用 API 总结

    文本整理了javascript操作DOM的一些常用的api,根据其作用整理成为创建,修改,查询等多种类型的api,主要用于复习基础知识,加深对原生js的认识. 基本概念 在讲解操作DOM的api之前, ...

  5. 【repost】Javascript操作DOM常用API总结

    Javascript操作DOM常用API总结 文本整理了javascript操作DOM的一些常用的api,根据其作用整理成为创建,修改,查询等多种类型的api,主要用于复习基础知识,加深对原生js的认 ...

  6. JavaScript 操作 DOM 总结

    基本概念 DOM 是 JavaScript 操作网页的接口,全称为"文档对象模型"(Document Object Model).它的作用是将网页转为一个 JavaScript 对 ...

  7. js操作svg整体缩放

    首先我们先创建一个svg整体布局,代码如下: <!DOCTYPE html> <html> <head> <title>js操作svg实现整体缩放< ...

  8. javascript操作多选下拉列表

    闲来无事,把javascript操作多选下拉列表有关的操作知识复习了一遍,代码附上 <%-- Created by IntelliJ IDEA. User: Administrator Date ...

  9. JavaScript操作XML

    JavaScript操作XML (一) JavaScript操作XML是通过XML DOM来完成的.那么什么是XML DOM呢?XML DOM 是: 用于 XML 的标准对象模型 用于 XML 的标准 ...

随机推荐

  1. 通过ES6实现的Ajax类

    个人学习用途而已,仅供参考. class Ajax { constructor(xhr) { xhr = window.XMLHttpRequest ? new XMLHttpRequest() : ...

  2. Invalid argument during startup: unknown conf file parameter : requirepass

    redis 设置来密码,出现来这个错误, 把配置文件那一行的 空格 去掉 # requirepass foobared 改成 requirepass 123456 因为# 和 requirepass ...

  3. DES加密(支持ARC与MRC)

    DES加密(支持ARC与MRC) 源文件: YXCrypto.h 与 YXCrypto.m // // YXCrypto.h // 用秘钥给字符串加密或者解密 // // Created by You ...

  4. 审计系统---堡垒机python下ssh的使用

    堡垒机python下ssh的使用 [堡垒机更多参考]http://www.cnblogs.com/alex3714/articles/5286889.html [paramiko的Demo实例]htt ...

  5. Linux alias命令详解

    alias:内置命令(没有路径),设置命令的别名,保护系统,方便 unalias:别名取消 常用的命令展示 临时设置 alias cp='cp –i' unalias cp      取消系统的别命令 ...

  6. LinkedHashSet 元素唯一,存储取出有序

      package cn.itcast_04; import java.util.LinkedHashSet; /* * LinkedHashSet:底层数据结构由哈希表和链表组成. * 哈希表保证元 ...

  7. 整体修改VS中C++工程和解决方案命名问题

    目标:将所有basestation相关的修改为PPPStream 首先,复制一份用于修改的解决方案文件夹,然后打开解决方案,如下图,左侧的两个basestation都重新命名为PPPStream. 第 ...

  8. 第二次作业--APP案例分析

    网易云音乐APP分析 第一部分 调研, 评测 1.APP打开界面简洁,一进入APP便能看到APP推荐的歌单,再使用的时候可以更多的了解新的歌曲 2.APP顶部分为三个板块为音乐管理.音乐推荐(音乐推荐 ...

  9. php实现二分查找

    <?php class BinSch{ /* * 二分查找,针对顺序表才有用 */ public function search($arr,$k,$low,$high){ if($low< ...

  10. [T-ARA][Apple is A]

    歌词来源:http://music.163.com/#/song?id=22704474 달콤달콤해 짜릿짜릿해 [tal-Kom-dal-Ko-mae jja-lid-jja-li-Tae] 반짝반 ...