在 SVG 中包含 JavaScript

Posted

技术标签:

【中文标题】在 SVG 中包含 JavaScript【英文标题】:Including JavaScript in SVG 【发布时间】:2011-07-19 17:04:19 【问题描述】:

我正在尝试通过将 javascript 嵌入 SVG 来使用 JavaScript 创建交互式 SVG 代码。我不知道这是否是正确的做法:

<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN"
"http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
<svg   version="1.1"
xmlns="http://www.w3.org/2000/svg"
onkeypress="move()">
<script type="text/javascript">
    <![CDATA[
    var x;
    var y;
    function move()
    
        x = new Number(svg.getElementsByTagName("circle")[0].getAttribute("cx"));
        y = new Number (svg.getElementsByTagName("circle")[0].getAttribute("cy"));
        switch (event.keyCode)
        
            case 119:
            y--;
            y = y.toString();
            svg.getElementsByTagName("circle").setAttribute("cy",y);
            break;
            case 115:
            y++;
            y = y.toString();
            svg.getElementsByTagName("circle").setAttribute("cy",y);
            break;
            case 97:
            x--;
            x = x.toString();
            svg.getElementsByTagName("circle").setAttribute("cx",x);
            break;
            case 100:
            x++;
            x = x.toString();
            svg.getElementsByTagName("circle").setAttribute("cx",x);
            break;
            default:
        
    
    ]]>
</script>
<rect x="0" y="0"   style="stroke-width:1; stroke:black; fill:white"></rect>
<circle cx="250" cy="250" r="50" stroke="red" stroke- fill="red"></circle>
</svg>

它应该有一个随wasd移动的球,但球没有移动。我做错了什么?

【问题讨论】:

【参考方案1】:

这是我写的工作版本:

<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN"
  "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
<svg version="1.1" xmlns="http://www.w3.org/2000/svg">
  <circle cx="250" cy="250" r="50" fill="red" />
  <script type="text/javascript"><![CDATA[
    var KEY =  w:87, a:65, s:83, d:68 ;
    var moveSpeed = 5;
    var circle = document.getElementsByTagName("circle")[0];
    var x = circle.getAttribute('cx')*1,
        y = circle.getAttribute('cy')*1;
    document.documentElement.addEventListener('keydown',function(evt)
      switch (evt.keyCode)
        case KEY.w:
          circle.setAttribute('cy',y-=moveSpeed);
          // Alternatively:
          // circle.cy.baseVal.value = (y-=moveSpeed);
        break;
        case KEY.s:
          circle.setAttribute('cy',y+=moveSpeed);
        break;
        case KEY.a:
          circle.setAttribute('cx',x-=moveSpeed);
        break;
        case KEY.d:
          circle.setAttribute('cx',x+=moveSpeed);
        break;
      
    ,false);
  ]]></script>
</svg>

一些注意事项:

    不要一次又一次地重新获得对圆圈的引用。使您的代码DRY 使其更健壮,更少的输入,并且(在这种情况下)更快地执行。

    编辑:如果您无法根据我上面的代码弄清楚如何执行此操作,请发布任何不适合您的代码。

    不要依赖全局的event 对象;那是旧的 IE 废话。使用传递给事件处理程序的事件对象。

    编辑:如果您在代码中引用event,而没有使用该名称的参数或局部变量,则您假设将有一个全局event 对象集。相反,请参阅我为您编写的代码,它显示事件处理程序传递了一个 event 对象。通过给它一个名称,例如我给它的名称evt,您将收到一个特定于您的事件处理程序的事件对象。

    由于您正在修改xy 变量,因此无需在每次按键时重新获取cxcy 属性。

    编辑:在您的原始代码和您接受的答案中,您在事件处理程序之外声明了var x,并且在事件处理程序的开头有x = ...,然后是@ 987654335@ 在其中一个事件处理程序中。您可以每次重新获取 x 的当前值(如您所做的那样)然后 setAttribute(...,x+1),或者(如我所做的那样)您之前只能获取属性 once 的值事件处理程序,然后假定每次处理关键事件时此值都是正确的。

    不要将 JavaScript 事件处理程序放在元素上,以编程方式附加它们。

    编辑:在您的 SVG 标记中,您有:&lt;svg ... onkeypress="move()"&gt;。将你的行为与你的标记混合在一起在 html 中是一个非常糟糕的主意,在 SVG 中也是一个糟糕的主意。不要使用onfoo="..." 属性来描述当事件发生在元素上时应该发生的事情,而是使用addEventListner() 通过代码附加事件处理程序,而无需编辑您的SVG 标记。

    在将数字设置为属性之前,无需将其强制转换为字符串。

    使用keydown 和我在上面提供的ASCII 事件代码,而不是keypress 和您使用的奇数,如果您希望它在所有浏览器中工作。

    编辑:您在another post 中抱怨您不能这样做,因为您希望在按住键时重复处理事件处理程序。请注意,您想要的行为是通过我在 Chrome、Safari、Firefox 和 IE 中的示例代码实现的(我没有要测试的 Opera)。换句话说,keydown 按你的意愿工作,不管你认为它应该如何工作。

编辑 2:如果您想在文档顶部包含一个脚本块,在所有元素都必须创建之前,您可以执行以下操作:

<svg ...>
  <script type="text/javascript">
    window.addEventListener('load',function()
      var circle = ...;
      document.rootElement.addEventListener('keydown',function(evt)
        ...
      ,false);
    ,false);
  </script>
  ...
</svg>

外部函数只会在页面加载后运行,因此您可以确定元素存在以引用它们。

【讨论】:

我建议使用 SVG DOM 方法来设置 cx 和 cy,但这看起来是一个很好的解决方案,除了那个小问题。 @Erik 你能提供更多关于你为什么建议的细节吗?无论是使用circle.setAttribute('cy',y) 还是circle.cy.baseVal.value=y,我都可以在 Chrome 中获得 250fps,在 FF3.6 中获得 100fps。您认为其中一个更快吗?您是否正在考虑与 SMIL 进行一些互动?【参考方案2】:

您可以通过将脚本标签声明为svg标签来将脚本js添加到svg代码中:

<svg version="1.1" id="loader-1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px"
       viewBox="0 0 50 50" style="enable-background:new 0 0 50 50;" xml:space="preserve">
     ...
  <script type="text/javascript">
        window.addEventListener('load',function()
        alert('Hi')
        )
    </script>   

</svg>

【讨论】:

【参考方案3】:

这适用于 Chrome。您遇到了一些错误,例如有时仅对 getElementsByTagName 进行索引。另外最大的问题是 onkeypress 属性没有绑定。

<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN"
"http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
<svg   version="1.1"
xmlns="http://www.w3.org/2000/svg"
>
<script type="text/javascript">
    <![CDATA[

    var x;
    var y;
    function move()
    
        x = new Number(document.getElementsByTagName("circle")[0].getAttribute("cx"));
        y = new Number (document.getElementsByTagName("circle")[0].getAttribute("cy"));
        switch (event.keyCode)
        
            case 119:
            y--;
            y = y.toString();
            document.getElementsByTagName("circle")[0].setAttribute("cy",y);
            break;
            case 115:
            y++;
            y = y.toString();
            document.getElementsByTagName("circle")[0].setAttribute("cy",y);
            break;
            case 97:
            x--;
            x = x.toString();
            document.getElementsByTagName("circle")[0].setAttribute("cx",x);
            break;
            case 100:
            x++;
            x = x.toString();
            document.getElementsByTagName("circle")[0].setAttribute("cx",x);
            break;
            default:
        
    
    document.documentElement.addEventListener("keypress", move);
    ]]>
</script>
<rect x="0" y="0"   style="stroke-width:1; stroke:black; fill:white"></rect>
<circle cx="250" cy="250" r="50" stroke="red" stroke- fill="red"></circle>
</svg>

【讨论】:

请注意,除非您将第三个参数明确添加到 addEventListener,否则 Firefox 会哭。那样很挑剔。 @mortalc 这个答案在 Firefox 中不起作用,而我的答案是。它还继续使用您的答案中不必要且不可取的代码。 我以前从未听说过 addEventListener!这就是为什么它不起作用! @mortalc 无意冒犯,但除此之外还有多种原因导致您的代码无法正常工作。 我不能不同意这一点,因为我不知道更好,但我只是说另一个对我有用。只有两件事让我感到困惑。 1)如果我像你在你的那样为 document.getElementsByTagName("circle")[0] 设置一个变量 circle 并使用它而不是每次都获取元素,它不起作用。并且 2) 如果我使用带有矩形的代码,并将 cx 和 cy 替换为 x 和 y,它就不起作用。但是由于某些奇怪的原因,如果我得到 cx 和 cy 但编辑 x 和 y,它会这样做。

以上是关于在 SVG 中包含 JavaScript的主要内容,如果未能解决你的问题,请参考以下文章

为啥我们应该在字体中包含 ttf、eot、woff、svg、...

在 Laravel 5 Blade 模板中包含 SVG 内容

SVG 下载导致文件中包含“无效的 SVG 代码”

在 Flexbox 中包含 SVG 图像

如何在 Nuxt 应用程序中包含内联 .svg

如何在我的 svg 中包含一个很棒的字体图标?