分享

面向浏览器的动态 SVG

 家住天地 2011-04-25

简介:  学习如何使用可缩放向量图形(SVG)的动态性为 Web 应用程序提供实用的交互效果。SVG 1.1 是一种描述二维向量图形的 XML 语言,提供了实用、灵活的 XML 图像格式。很多 SVG 特性具有动态效果,包括集成到 Web 浏览器中的特性。本文以上一期教程总介绍的 SVG 基本知识为基础。


 

更多的 ECMAScript 操纵

有时候,无论控制独立的 SVG 或者 XHTML 中的 SVG,仅仅操纵类值还不够。幸运的是,通过脚本基本上能实现任何需要的功能。这一节介绍用于 SVG 的更棘手的脚本操作。

直接修改样式细节

利用 CSS 属性操作的 CSS DOM 绑定,可以根据需要调整对象的外观。清单 9(tutorial-files.zip 中的 eg_3_1.svg)中的 SVG 文件画了一个圆,每点击它一次颜色都会变得更深:


清单 9. 点击变红的 SVG 圆
            <?xml version="1.0" encoding="utf-8"?>
            <svg version="1.1" baseProfile="full"
            xmlns="http://www./2000/svg">
            <title>SVG circle that responds to clicks
            by turning redder</title>
            <defs>
            <style type="text/css"><![CDATA[
            #target { fill: black; }
            ]]></style>
            <script type="application/javascript"><![CDATA[
            var redness = 0;
            function redden()
            {
            var target;
            target = document.getElementById('target');
            if ( ( redness <= 255 ) ) {
            redness += 32;
            target.style.setProperty("fill",
            "rgb(" + redness + ",0,0)", "important");
            }
            }
            ]]></script>
            </defs>
            <circle id="target" onclick="redden()" cx="3cm"
            cy="3cm" r="2cm"/>
            </svg>
            

使用 setProperty 方法设置 CSS 样式属性的值。在这里,fill 属性被设置成 RBG 颜色值,标准 CSS/DOM 形式的字符串 —— rgb(255,255,255) 对应白色,改变数字就能改变颜色。清单 9 中绿色和蓝色分量总是 0,因为我们只想改变圆的红色分量。第三个参数决定了样式表修改的优先级。基本上使用的总是 important

在浏览器中加载 清单 9。初始显示结果如图 7 所示。反复单击圆可以看到它的颜色从黑慢慢变红。在这个事件处理程序中尝试修改其他样式属性。


图 7. 清单 9 在浏览器中的输出结果
清单 9 在浏览器中的输出结果

操纵其他 SVG 属性

上一期教程(请参阅 参考资料)中提到,SVG 使用 XML 属性作为图形的基本属性,比如位置和大小。清单 10(tutorial-files.zip 中的 eg_3_2.svg)中的 SVG 文件画了两个圆,点击的时候会不断变大:


清单 10. 点击变大的 SVG 圆
            <?xml version="1.0" encoding="utf-8"?>
            <svg version="1.1" baseProfile="full"
            xmlns="http://www./2000/svg">
            <title>SVG circle that gets larger when clicked</title>
            <defs>
            <style type="text/css"><![CDATA[
            #touchable circle.left { fill: orange; }
            #touchable circle.right { fill: lime; }
            ]]></style>
            <script type="application/javascript"><![CDATA[
            var size = 2;
            function grow(evt)
            {
            var target;
            target = evt.target;
            size += 0.25;
            target.setAttribute('r', size+'cm');
            }
            ]]></script>
            </defs>
            <g id="touchable">
            <circle class="left" onclick="grow(evt)"
            cx="3cm" cy="3cm" r="2cm"/>
            <circle class="right" onclick="grow(evt)"
            cx="3cm" cy="3cm" r="2cm" transform="translate(200,0)"/>
            </g>
            </svg>
            

脚本维护一个全局变量 size。但是两个圆不是独立变大的。如果点击其中一个圆几次,然后再单击另一个,后者将直接跳到和前者相同的大小。这是因为两个圆共享脚本中的 size 变量。在事件处理程序中,size 增加了,直接操纵 r 属性改变圆的半径。在浏览器中加载 清单 10。初始显示结果如图 8 所示。反复单击两个圆可以看到它们不断变大。


图 8. 清单 10 在浏览器中的显示结果
清单 10 在浏览器中的显示结果

动态变换

SVG 变换是精确控制对象外观和位置的关键,在通过脚本操纵对象时同样有用。清单 11(tutorial-files.zip 中的 eg_3_3.svg)中的 SVG 文件画了一个圆,单击的时候会移动到右侧。


清单 11. 点击滑动的 SVG 圆
            <?xml version="1.0" encoding="utf-8"?>
            <svg version="1.1" baseProfile="full"
            xmlns="http://www./2000/svg">
            <title>SVG circle that slides to the right when clicked</title>
            <defs>
            <style type="text/css"><![CDATA[
            circle { fill: orange; }
            ]]></style>
            <script type="application/javascript"><![CDATA[
            var x = 0;
            function move(evt)
            {
            var target;
            target = evt.target;
            x += 25;
            target.setAttribute('transform', 'translate(' + x + ',0)');
            }
            ]]></script>
            </defs>
            <circle onclick="move(evt)" cx="3cm" cy="3cm" r="2cm"/>
            </svg>
            

这与前面看到的 DOM 属性操作非常类似。可以在 transform 属性中使用专门的类似函数的符号来表示变换,如旋转、移位、缩放和扭曲。还可以指定完整的变换矩阵。在浏览器中加载 清单 11。最初看到的只有一个橙色的圆。反复单击这个圆可以看到它会滑动到右侧。


创建对象

除了操纵已有的对象外,还可以使用脚本创建新的对象。我们可以在独立 SVG 中用脚本创建对象,但是稍加阐述后即回到 XHTML 中内嵌的 SVG。清单 12(tutorial-files.zip 中的 eg_3_4.xhtml)中的 XHTML 文件有一个按钮,单击它可以创建圆:


清单 12. 单击按钮在 XHTML 中动态创建圆
            <?xml version="1.0" encoding="UTF-8"?>
            <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN"
            "http://www./TR/xhtml1/DTD/xhtml1-strict.dtd">
            <html xml:lang="en"
            xmlns="http://www./1999/xhtml"
            xmlns:svg="http://www./2000/svg">
            <head>
            <title>XHTML example for creating circles dynamically
            by clicking a button</title>
            <style type="text/css"><![CDATA[
            circle {
            fill: lime;
            stroke: black;
            stroke-width: 0.2cm;
            }
            ]]></style>
            <script type="application/javascript"><![CDATA[
            function create_circle()
            {
            var svg = document.getElementById('diagram');
            var cx = Math.random()*10;
            var cy = Math.random()*4;
            var r = Math.round(Math.random()*4);
            circle = document.createElementNS('http://www./2000/svg', 'circle')
            circle.setAttribute('cx', cx+'cm')
            circle.setAttribute('cy', cy+'cm')
            circle.setAttribute('r', r+'cm')
            svg.appendChild(circle)
            }
            ]]></script>
            </head>
            <body>
            <svg:svg id='diagram' version="1.1" width="600" height="220">
            <svg:title>Create circles dynamically</svg:title>
            </svg:svg>
            <button onclick="create_circle();">Create circle</button>
            </body>
            </html>
            

用户单击按钮时会调用 create_circle 函数。该函数首先选择内嵌的 SVG 元素作为要创建的圆形的父元素。该元素包含 id 属性,很容易检索。然后代码使用 ECMAScript 数学库创建圆形的位置和半径。random 函数生成介于 0 和 1 之间的一个随机浮点数,用它乘上最大值。对于半径,还对结果进行舍入以保证圆的大小只能取四个数。然后加上 cm 单位指示符。如果省略,则与通常一样使用默认单位 —— 像素。使用名称空间感知方法 createElementNS 创建圆对象,为新元素指定必要的 SVG 名称空间。剩下的就是设置这些属性了。再回到不支持名称空间的 setAttribute 方法,因为需要的属性不在任何名称空间中。最后,使用前面检索的 svg 父元素将 SVG 对象添加到文档中。

在浏览器中加载 清单 12。最初看到的窗口只有一个按钮,但是如果单击该按钮,就会看到绿色的大小不定的圆出现。要注意,浏览器会根据内嵌 SVG 指定的高和宽剪裁圆。


创建具有动态行为的对象

按照上一节的方法创建 SVG 对象的时候,没有理由不能创建这些对象自己的动态行为。清单 13(tutorial-files.zip 中的 eg_3_5.xhtml)中的 XHTML 文件有一个按钮,单击它可以创建圆形,而这些圆在单击的时候又会改变自己的边界:


清单 13. 清单 12 的改进版,创建的圆能够响应点击
            <?xml version="1.0" encoding="UTF-8"?>
            <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN"
            "http://www./TR/xhtml1/DTD/xhtml1-strict.dtd">
            <html xml:lang="en"
            xmlns="http://www./1999/xhtml"
            xmlns:svg="http://www./2000/svg">
            <head>
            <title>Creating SVG objects with event handlers</title>
            <style type="text/css"><![CDATA[
            circle { fill: orange; }
            circle.open {
            stroke: none;
            }
            circle.closed {
            stroke: black;
            stroke-width: 0.2cm;
            }
            ]]></style>
            <script type="application/javascript"><![CDATA[
            function create_circle()
            {
            var svg = document.getElementById('diagram');
            var cx = Math.random()*10;
            var cy = Math.random()*4;
            var r = Math.round(Math.random()*4);
            circle = document.createElementNS('http://www./2000/svg', 'circle')
            circle.setAttribute('cx', cx+'cm')
            circle.setAttribute('cy', cy+'cm')
            circle.setAttribute('r', r+'cm')
            circle.setAttribute('class', 'open')
            svg.appendChild(circle)
            //The added line to set up the event handler
            circle.addEventListener("click", toggle, false)
            }
            function toggle(evt)
            {
            var target;
            target = evt.target;
            if (target.getAttributeNS('', 'class') == 'closed')
            target.setAttributeNS('', 'class', 'open');
            else
            target.setAttributeNS('', 'class', 'closed');
            }
            ]]></script>
            </head>
            <body>
            <svg:svg id='diagram' version="1.1" width="600" height="220">
            <svg:title>Create circles dynamically</svg:title>
            </svg:svg>
            <button onclick="create_circle();">Create circle</button>
            </body>
            </html>
            

其中的脚本和样式表大部分前面已经遇到过。其中有一行是新增加的,即使用 addEventListener 为鼠标单击事件建立处理程序 toggle。在浏览器中加载 清单 13。可以看到与 清单 12 类似的结果,只不过创建的圆形没有边框,用鼠标点击可以增加和消除边界线。


删除对象

自己创建的东西可能还希望自己删除。这一节结尾的清单 14(tutorial-files.zip 中的 eg_3_6.xhtml)类似于 清单 13,但是通过点击可以删除所创建的圆形:


清单 14. 清单 12 的改进版,用户可以删除创建的圆形
            <?xml version="1.0" encoding="UTF-8"?>
            <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN"
            "http://www./TR/xhtml1/DTD/xhtml1-strict.dtd">
            <html xml:lang="en"
            xmlns="http://www./1999/xhtml"
            xmlns:svg="http://www./2000/svg">
            <head>
            <title>Creating SVG objects with event handlers to trigger removal</title>
            <style type="text/css"><![CDATA[
            circle {
            fill: orange;
            stroke: black;
            stroke-width: 0.2cm;
            }
            ]]></style>
            <script type="application/javascript"><![CDATA[
            function create_circle()
            {
            var svg = document.getElementById('diagram');
            var cx = Math.random()*10;
            var cy = Math.random()*4;
            var r = Math.round(Math.random()*4);
            circle = document.createElementNS('http://www./2000/svg', 'circle')
            circle.setAttribute('cx', cx+'cm')
            circle.setAttribute('cy', cy+'cm')
            circle.setAttribute('r', r+'cm')
            circle.setAttribute('class', 'open')
            svg.appendChild(circle)
            circle.addEventListener("click", pop, false)
            }
            function pop(evt)
            {
            var target;
            target = evt.target;
            //The new circle click response: delete the circle element
            target.parentNode.removeChild(target);
            }
            ]]></script>
            </head>
            <body>
            <svg:svg id='diagram' version="1.1" width="600" height="220">
            <svg:title>Create and pop balloons</svg:title>
            </svg:svg>
            <button onclick="create_circle();">Make a balloon</button>
            <p>(Click a balloon to pop it)</p>
            </body>
            </html>
            

大部分代码都是熟悉的。主要的变化是为单击事件增加了一个事件处理程序,它使用 parentNode 获得父元素,然后用 removeChild 删除单击的圆。

    本站是提供个人知识管理的网络存储空间,所有内容均由用户发布,不代表本站观点。请注意甄别内容中的联系方式、诱导购买等信息,谨防诈骗。如发现有害或侵权内容,请点击一键举报。
    转藏 分享 献花(0

    0条评论

    发表

    请遵守用户 评论公约

    类似文章 更多