svg

前阵子学习了一下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属性。

<html>
<head>
    <title>Nested SVG</title>
</head>
<body>
 
<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" 
    version="1.1" width="20" height="20">
<circle id="c" cx="10" cy="10" r="7" fill="green"/>
</svg>
<script type="text/javascript">
 
var c = document.getElementById('c');
c.setAttribute('fill','blue');
 
</script>
</body>
</html>

不过大多时候,SVG并不会直接嵌套在HTML之中重现出来,更多的会选择将其作为图片通过img标签引进,或者当做背景图片在CSS中通过url引入。对于这种情况,SVG只是单纯的当做图片来使用而且一个XML类型的文档,所以也就无法使用JavaScript来操作它们。

还有一类方法则是通过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()则返回null

contentDocument

获取了SVG的document之后,就可以像往常那样获取内部元素属性、绑定事件等等。还可以定义一个document为参数的方法形成局部变量,要对某个引入SVG文档进行操作时就获取该文档的document对象传入,想获取主文档的对象时就使用window.document即可。

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

当然了使用上边一系列属性和方法都有一个大前提:要满足同源策略(Same-origin policy)。若引入的SVG文档是来自于其它站点的,那么浏览器就会禁止获取document对象。

操作SVG的元素

SVG作为XML的方言,不同于HTML松散的标签结构和格式,它严格遵循XML的语法格式,所以开始标签都要有对应的结束标签,所有标签都要被svg标签包含在内。另外在HTML经常被忽略的一个知识就是:XML是有命名空间(namespace)的。命名空间在通过JavaScript创建SVG元素对象的时候就引起了一些麻烦。

一般的在HTML中若想通过JavaScript创建一个元素对象的话,代码类似如下:

var inp = document.createElement('input');
inp.type = 'button';
inp.value = 'button';
inp.name = 'button';
 
var con = document.getElementById('container');
con.appendChild(inp);

但是使用相同的方法,创建SVG元素并添加到SVG文档中的话, 该元素并不会呈现出来。

<svg xmlns="http://www.w3.org/2000/svg"
    xmlns:xlink="http://www.w3.org/1999/xlink"
    version="1.1" width="20" height="20">
<script type="text/javascript">
    var c = document.createElement('circle');
    c.cx = 10;
    c.cy = 10;
    c.r = 7;
    c.fill = 'green';
 
    document.rootElement.appendChild(c);
</script>
</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的绿色的圆点。

<svg xmlns="http://www.w3.org/2000/svg"
    xmlns:xlink="http://www.w3.org/1999/xlink"
    version="1.1" width="20" height="20">
    <script type="text/javascript">
 
        var c = document.createElementNS('http://www.w3.org/2000/svg','circle');
        c.setAttribute('cx', 10);
        c.setAttribute('cy', 10);
        c.r.baseVal.value = 7;
        c.setAttribute('fill', 'green');
 
        document.rootElement.appendChild(c);
    </script>
</svg>

除了元素有命名空间,有些属性也有其特定的命名空间。比如在HTML极为常用的a标签的,在SVG中又有存在,但是对于其href属性,在SVG之中就必须加上xmlns:前缀来指定其命名空间了。对于设置这些属性也需要用setAttributeNS()方法并将”http://www.w3.org/1999/xlink“作为第一参数传入。

<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink"
    version="1.1" width="20" height="20">
    <script type="text/javascript">
 
        var a = document.createElementNS('http://www.w3.org/2000/svg','a');
        a.setAttributeNS('http://www.w3.org/1999/xlink',
                     'xlink:href', 
                     'http://blog.iderzheng.com/');
 
        var c = document.createElementNS('http://www.w3.org/2000/svg','circle');
        c.setAttribute('cx', 10);
        c.setAttribute('cy', 10);
        c.r.baseVal.value = 7;
        c.setAttribute('fill', 'green');
 
        a.appendChild(c);
 
        document.rootElement.appendChild(a);
    </script>
</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()这样的方式。

function click(e) {
    // rootElement is specific to SVG document
    // documentElemnt is to any XML document include HTML
    // they both can retrieve the root element of a document 
    var r = document.rootElement || document.documentElement,
    pt = r.createSVGPoint(),
    im = r.getScreenCTM().inverse(); // inverse of tranforma matrix
 
    // set point with window coordination
    pt.x = e.clientX;
    pt.y = e.clientY;
 
    // convert point to SVG coordination
    var p = pt.matrixTransform(im);{
}

本片博客摘抄自iderzheng

©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 211,194评论 6 490
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 90,058评论 2 385
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 156,780评论 0 346
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 56,388评论 1 283
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 65,430评论 5 384
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 49,764评论 1 290
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 38,907评论 3 406
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 37,679评论 0 266
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 44,122评论 1 303
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 36,459评论 2 325
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 38,605评论 1 340
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 34,270评论 4 329
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 39,867评论 3 312
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 30,734评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,961评论 1 265
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 46,297评论 2 360
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 43,472评论 2 348

推荐阅读更多精彩内容

  • 使用XML描述的矢量文件W3C标准(1.1):http://www.w3.org/TR/SVG11/浏览器支持情况...
    没汁帅阅读 5,973评论 0 16
  •   DOM 1 级主要定义的是 HTML 和 XML 文档的底层结构。   DOM2 和 DOM3 级则在这个结构...
    霜天晓阅读 1,427评论 1 3
  • 一:什么是SVG? 对于SVG的定义如下: ①:SVG 指的是可伸缩矢量图形 (Scalable Vector G...
    GreenHand1阅读 798评论 0 1
  • HTML 5 HTML5概述 因特网上的信息是以网页的形式展示给用户的,因此网页是网络信息传递的载体。网页文件是用...
    阿啊阿吖丁阅读 3,858评论 0 0
  • 一.什么是SVG? SVG 指的是可伸缩矢量图形 (Scalable Vector Graphics),它用来定义...
    nightZing阅读 17,034评论 11 62