28、SVG 与 JavaScript 结合的应用及解决方案

SVG与JavaScript交互应用

SVG 与 JavaScript 结合的应用及解决方案

1. 向 SVG 文件添加 JavaScript

1.1 问题描述

想要向 SVG 文件或元素添加 JavaScript。

1.2 解决方案

在 SVG 中,JavaScript 包含在 script 元素中,与 XHTML 一样。同时,也可使用 DOM 方法操作 SVG 元素。不过,SVG 是 XML,因此 SVG 文件或元素中的脚本块必须用 CDATA 部分包围实际脚本,示例代码如下:

<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<svg xmlns="http://www.w3.org/2000/svg"
xmlns:xlink="http://www.w3.org/1999/xlink" width="600" height="600">
  <script type="text/ecmascript">
    <![CDATA[
      // set element onclick event handler
      window.onload=function () {
         var square = document.getElementById("square");
         // onclick event handler, change circle radius
         square.onclick = function() {
            var color = this.getAttribute("fill");
            if (color == "#ff0000") {
               this.setAttribute("fill", "#0000ff");
            } else {
               this.setAttribute("fill","#ff0000");
            }
         }
      }
    ]]>
  </script>
  <rect id="square" width="400" height="400" fill="#ff0000"
   x="10" y="10" />
</svg>

1.3 讨论

SVG 是 XML,嵌入脚本到 XML 时必须遵循相关规则,即提供 script 标签内的脚本类型,并将脚本内容包装在 CDATA 块中。DOM 方法(如 document.getElementById getAttribute setAttribute )不仅适用于 HTML,也适用于包括 SVG 在内的任何 XML 文档。新的 SVG 特定属性 fill 是 SVG 形状元素(如 rect )的标准颜色属性之一。

该解决方案是一个独立的 SVG 文件,扩展名为 .svg 。若将 SVG 嵌入到作为 application/xhtml+xml 提供的 XHTML 文件中,颜色变化动画效果相同,示例代码如下:

<!DOCTYPE html PUBLIC
    "-//W3C//DTD XHTML 1.1 plus MathML 2.0 plus SVG 1.1//EN"
    "http://www.w3.org/2002/04/xhtml-math-svg/xhtml-math-svg.dtd">
<html xmlns="http://www.w3.org/1999/xhtml"
      xmlns:svg="http://www.w3.org/2000/svg"
      xmlns:xlink="http://www.w3.org/1999/xlink" xml:lang="en">
<head>
<title>Accessing Inline SVG</title>
<meta http-equiv="Content-Type"
content="application/xhtml+xml; charset=utf-8" />
</head>
<body>
<svg:svg width="600" height="600">
  <script type="text/ecmascript">
    <![CDATA[
      // set element onclick event handler
      window.onload=function () {
         var square = document.getElementById("square");
         // onclick event handler, change circle radius
         square.onclick = function() {
            var color = this.getAttribute("fill");
            if (color == "#ff0000") {
               this.setAttribute("fill","#0000ff");
            } else {
               this.setAttribute("fill","#ff0000");
            }
         }
      }
    ]]>
  </script>
  <svg:rect id="square" width="400" height="400" fill="#ff0000"
 x="10" y="10" />
</svg:svg>
</body>
</html>

Chrome、Safari、Opera 和 Firefox 都支持 SVG,IE8 不支持,但 IE9 会支持。

2. 从网页脚本访问 SVG

2.1 问题描述

想要从网页内的脚本修改 SVG 元素的内容。

2.2 解决方案

  • 直接嵌入 SVG :若 SVG 直接嵌入网页,使用与访问其他网页元素相同的功能访问元素及其属性,示例代码如下:
var square = document.getElementById("ssquare");
square.setAttributeNS(null, "width", "500");
  • 外部 SVG 文件 :若 SVG 在通过 object 元素嵌入页面的外部 SVG 文件中,需要获取外部 SVG 文件的文档对象才能访问 SVG 元素。由于不同浏览器处理方式不同,需要进行对象检测,示例代码如下:
// set element onclick event handler
window.onload=function () {
   var object = document.getElementById("object");
   var svgdoc;
   try {
      svgdoc = object.contentDocument;
   } catch(e) {
      try {
         svgdoc = object.getSVGDocument();
       } catch (e) {
          alert("SVG in object not supported in your environment");
       }
   }
   if (!svgdoc) return;
   var square = svgdoc.getElementById('square');
   square.setAttributeNS(null, "width", "500");

2.3 讨论

第一种方法用于访问嵌入 XHTML 文件的 SVG,可使用与访问 HTML 元素相同的方法访问 SVG 元素。由于 XHTML 中的 SVG 支持命名空间,即使不使用命名空间(设置为 null ),也使用 DOM 方法的命名空间版本。

第二种方法更复杂,依赖于检索 SVG 文档的文档对象。首先尝试访问 object 的 contentDocument 属性,若失败则尝试使用 getSVGDocument 对象方法。获取 SVG 文档对象后,可使用与网页原生元素相同的 DOM 方法。此代码适用于除 IE 外的所有支持的浏览器。

以下是另一种将 SVG 添加到网页并从 HTML 脚本访问 SVG 元素的示例代码:

<!DOCTYPE html>
<head>
<title>SVG in Object</title>
<meta charset="utf-8" />
</head>
<body>
<object id="object" data="rect.svg"
style="padding: 20px; width: 600px; height: 600px">
<p>No SVG support</p>
</object>
<script type="text/javascript">
  var object = document.getElementById("object");
  object.onload=function() {
      var svgdoc;
      // get access to the SVG document object
      try {
         svgdoc = object.contentDocument;
      } catch(e) {
         try {
            svgdoc = object.getSVGDocument();
          } catch (e) {
            alert("SVG in object not supported in your environment");
          }
      }
      if (!svgdoc) return;
      var r = svgdoc.rootElement;
      // get SVG element and modify
      var square = svgdoc.getElementById('square');
      square.onclick = function() {
         //SVG supports namespaces
         var width = parseFloat(square.getAttributeNS(null,"width"));
         width-=50;
         square.setAttributeNS(null,"width",width);
         var color = square.getAttributeNS(null,"fill");
         if (color == "blue") {
             square.setAttributeNS(null,"fill","yellow");
             square.setAttributeNS(null,"stroke","green");
         } else {
             square.setAttributeNS(null,"fill","blue");
             square.setAttributeNS(null,"stroke","red");
         }
      }
  }
</script>
</body>

除了获取 SVG 文档的不同方法外,还需处理浏览器在 onload 事件处理程序工作方式上的差异。Firefox 和 Opera 在所有文档内容(包括 object 元素中的 SVG)加载完成后触发窗口的 onload 事件处理程序,而 Safari 和 Chrome 可能由于共享 WebKit 核心,在 SVG 加载完成之前触发窗口的 onload 事件处理程序。

3. 在 Internet Explorer 中模拟 SVG

3.1 问题描述

希望 Internet Explorer 用户能够看到 object 元素中或嵌入页面的 SVG。

3.2 解决方案

使用 SVGWeb 等库来促进 SVG 的显示。
- 通过 object 元素引入 SVG :使用以下语法:

<!--[if IE]>
<object src="graphic.svg" classid="image/svg+xml"
width="200" height="200"
id="svgObject">
<![endif]-->
<!--[if !IE>-->
<object data="graphic.svg" type="image/svg+xml width="200"
height="200"
id="svgObject">
<!--<![endif]-->
</object>

条件注释对于 SVGWeb 正确发挥作用是必要的。
- 嵌入 SVG :只需添加 SVGWeb 库,并将 SVG 包含在 script 元素中:

<script type="image/svg+xml">
   <svg...>
      ...
   </svg>
</script>

3.3 讨论

目前 Internet Explorer 不支持 SVG,微软承诺在 2011 年左右发布的 IE9 中支持 SVG 和 XHTML。在此之前,可使用 SVGWeb 等库。SVGWeb 通过在 Flash 中模拟 SVG 来工作,由于 Flash 在大多数网站上几乎无处不在,大多数用户无需安装任何额外的插件。

要将 SVGWeb 集成到网页和应用程序中,下载并解压源代码后,将 src 目录加载到网站,并在网页中包含对 SVGWeb JavaScript 的链接:

<script src="src/svg.js" data-path="src/"></script>

此标签假设 SVGWeb 代码仍在 src 目录中,且库与网页不在同一目录。如果库位于不同位置,需要提供 data-path 自定义数据属性并指向库的相对位置。可在 http://code.google.com/p/svgweb/ 下载 SVGWeb 并查看手册和其他帮助。Ample SDK 是另一个为 IE 提供 SVG 支持的优秀库。

3.4 流程图

graph TD;
    A[开始] --> B{是否为 IE 浏览器};
    B -- 是 --> C[使用条件注释引入 SVG];
    B -- 否 --> D[正常引入 SVG];
    C --> E[添加 SVGWeb 库];
    D --> E;
    E --> F[完成 SVG 显示];

3.5 表格

浏览器 SVG 支持情况
Chrome 支持
Safari 支持
Opera 支持
Firefox 支持
IE8 不支持
IE9 支持

4. 启用嵌入 HTML 中的交互式 SVG

4.1 问题描述

想要将 SVG 直接嵌入 HTML 页面而无需使用 XHTML。

4.2 解决方案

有两种选择:
- 使用 HTML5 :创建网页并等待所有目标浏览器支持 HTML 中的 SVG。
- 使用 JavaScript 库 :如 SVGWeb 来包装 SVG,示例代码如下:

<script type="image/svg+xml">
   <svg...>
      ...
   </svg>
</script>

4.3 讨论

以前,要将 SVG 直接嵌入网页,必须使用 XHTML 而非 HTML。HTML5 现在支持在 HTML 中使用 SVG,但对此更改的支持仍然有限。在 Recipe 15.6 中引入了 SVGWeb 以在 Internet Explorer 中启用 SVG 支持,该库还支持将 SVG 直接嵌入 HTML 页面。在 HTML5 文档中对 SVG 的支持更广泛之前(目前仅 Firefox 3.6 及更高版本支持),应使用 SVGWeb 等库。

如果直接在 HTML5 中嵌入 SVG,需要注意一个主要区别:HTML5 中不支持命名空间。SVG 命名空间绑定到 svg 元素,MathML 命名空间绑定到 math 元素,但对这些以及其他 SVG 和 MathML 元素的支持是硬编码到 HTML5 规范中的,而不是由于命名空间支持而优雅集成的。这可能会影响 JavaScript 应用程序。

以下是一个 SVG 嵌入 HTML5 并作为 text/html 提供的示例代码:

<!DOCTYPE html>
<head>
<title>SVG</title>
<meta charset="utf-8" />
<script>
      // set element onclick event handler
      window.onload=function () {
         var circle = document.getElementById('redcircle');
         // onclick event handler, change circle radius
         circle.onclick = function() {
            var r = parseInt(this.getAttributeNS(null,"r"));
            r-=10;
            circle.setAttributeNS("","r",r);
            var  dc =
  document.getElementsByTagNameNS("http://purl.org/dc/elements/1.1/",
                 "title");
            for (var i = 0; i < dc.length; i++) {
               var str = dc.item(i).namespaceURI + " " +
               dc.item(i).prefix + " " + dc.item(i).localName + " " +
                              dc.item(i).textContent;
               alert(str);
            }
        }
      }
</script>
</head>
<body>
<h1>SVG</h1>
<p>This is <code>text/html</code>!</p>
<h2>SVG</h2>
<svg id="svgelem"
     height="800" xmlns="http://www.w3.org/2000/svg">
       <circle id="redcircle" cx="300" cy="300" r="300" fill="red" />
  <metadata>
    <rdf:RDF xmlns:cc="http://web.resource.org/cc/"
xmlns:dc="http://purl.org/dc/elements/1.1/"
xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#
">
      <cc:Work rdf:about="">
        <dc:title>Sizing Red Circle</dc:title>
        <dc:description></dc:description>
        <dc:subject>
          <rdf:Bag>
            <rdf:li>circle</rdf:li>
            <rdf:li>red</rdf:li>
            <rdf:li>graphic</rdf:li>
          </rdf:Bag>
        </dc:subject>
        <dc:publisher>
          <cc:Agent rdf:about="http://www.openclipart.org">
            <dc:title>Testing RDF in SVG</dc:title>
          </cc:Agent>
        </dc:publisher>
        <dc:creator>
          <cc:Agent>
            <dc:title id="title">Testing</dc:title>
          </cc:Agent>
          </dc:creator>
        <dc:rights>
          <cc:Agent>
            <dc:title>testing</dc:title>
          </cc:Agent>
          </dc:rights>
        <dc:date></dc:date>
        <dc:format>image/svg+xml</dc:format>
     <dc:type rdf:resource="http://purl.org/dc/dcmitype/StillImage"/>
 <cc:license rdf:resource="http://web.resource.org/cc/PublicDomain"/>
        <dc:language>en</dc:language>
      </cc:Work>
     <cc:License rdf:about="http://web.resource.org/cc/PublicDomain">
 <cc:permits rdf:resource="http://web.resource.org/cc/Reproduction"/>
 <cc:permits rdf:resource="http://web.resource.org/cc/Distribution"/>
<cc:permits
rdf:resource="http://web.resource.org/cc/DerivativeWorks"/>
      </cc:License>
    </rdf:RDF>
  </metadata>
  </svg>
</body>

在该示例中,SVG 元素嵌入到作为 text/html 提供的 HTML5 网页中。点击红色圆圈会更改圆圈尺寸,使用 document.getElementsByTagName 的命名空间版本,并传入 null 作为命名空间。脚本还访问 SVG 中所有都柏林核心命名空间(dc)的标题并在警报中显示它们。

当点击圆圈时,它会调整大小,但没有任何内容输出。这是因为页面作为 HTML 提供,而命名空间仅在基于 XML 的格式(如 XHTML)中正常工作。如果将代码中的以下部分:

var  dc =
document.getElementsByTagNameNS("http://purl.org/dc/elements/1.1/",
"title");

改为:

var  dc = document.getElementsByTagName("dc:title");

将获得对 SVG 元数据部分中所有四个 dc:title 元素的引用。

如果将示例转换为 XHTML,通过在 head 元素之前添加具有默认命名空间的 html 元素:

<html xmlns="http://www.w3.org/1999/xhtml" lang="en" xml:lang="en">

然后添加结束 HTML 标签,并将脚本块包装在 CDATA 部分中:

<script>
//<![CDATA[
...
//]]>
</script>

运行应用程序时,将使用原始的 document.getElementsByTagNS 获取 dc:title 值,但警报消息与 HTML 结果不同。

4.4 表格:HTML 与 XHTML 中元素属性对比

属性 HTML 版本 XHTML 版本
namespaceURI SVG 命名空间 正确的命名空间
prefix null 前缀
localName 前缀 + 元素名 元素名(去掉前缀)
textContent 相同 相同

4.5 流程图

graph TD;
    A[开始] --> B{选择嵌入方式};
    B -- HTML5 --> C{等待浏览器支持};
    B -- SVGWeb --> D[添加 SVGWeb 库];
    C --> E[嵌入 SVG];
    D --> E;
    E --> F[完成嵌入];

4.6 SVGWeb 的优势及注意事项

SVGWeb 不仅能在 HTML 文档(不仅是 HTML5 文档)中启用嵌入式 SVG 的支持,还能纠正命名空间问题。在上述 HTML 文档示例中添加 SVGWeb 库,并将 SVG 元素包装在具有 SVG MIME 类型的 script 标签中:

<script src="svgweb/src/svg.js" data-path="svgweb/src/"></script>
...
<script type="image/svg+xml">
   <svg id="svgelem"
     height="800" xmlns="http://www.w3.org/2000/svg">
      ...
   </svg>
</script>

现在,使用 SVGWeb 辅助的 HTML 页面获得的结果与 XHTML 页面相同,可以使用 DOM 方法的命名空间版本(如 document.getElementsByTagNameNS )并获得相同的结果。

然而,该应用程序在 IE8 中不起作用。原因是当在支持 SVG 的浏览器(如 Safari 或 Opera)中使用 SVGWeb 时,SVGWeb 在 XML 上下文中创建 SVG,但它仍然是 SVG。而在 IE8 中,SVGWeb 将图形创建为 Flash 而非 SVG。

因为 SVGWeb 将 SVG 创建为 Flash,所以还必须将命名空间定义移动到外部 SVG 元素:

<svg id="svgelem"
     height="800"
     xmlns="http://www.w3.org/2000/svg"
     xmlns:cc="http://web.resource.org/cc/"
     xmlns:dc="http://purl.org/dc/elements/1.1/"
     xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#">
 ...
</svg>

事件处理也通过 SVGWeb 管理,这意味着在使用较旧的 DOM Level 0 事件处理时会得到意外结果:

circle.onclick=function() {
...
}

相反,SVGWeb 为窗口对象和所有 SVG 元素提供跨浏览器的 addEventListener 功能。并且,不是捕获窗口的加载事件,而是捕获专门的事件 SVGLoad

window.addEventListener("SVGLoad",functionName,false);

由于 SVGWeb 为 IE8 实现的 addEventListener 函数使用了 IE 的 attachEvent ,应用程序也无法访问对象上下文,而是使用外部元素引用。以下是包含所有这些更改并在 IE8 以及 Safari、Chrome、Opera 和 Firefox 中都能工作的最终脚本块:

<script>
  // set element onclick event handler
  window.addEventListener('SVGLoad', function () {
     var circle = document.getElementById("redcircle");
     // onclick event handler, change circle radius
     circle.addEventListener('click', function(evt) {
     // reference circle, rather than this
        var r = parseInt(circle.getAttribute("r"));
        r-=10;
        circle.setAttribute("r",r);
        var  dc = document.getElementsByTagNameNS("http://purl.org/dc/elements/1.1/",
        "title");
        for (var i = 0; i < dc.length; i++) {
           var str = dc.item(i).namespaceURI + " " +
           dc.item(i).prefix + " " + dc.item(i).localName + " " +
                          dc.item(i).textContent;
           alert(str);
        }
     });
  });
</script>
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值