【问题标题】:Including JavaScript in SVG在 SVG 中包含 JavaScript
【发布时间】: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 width="100%" height="100%" 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" height="500" width="500" style="stroke-width:1; stroke:black; fill:white"></rect>
<circle cx="250" cy="250" r="50" stroke="red" stroke-width="1" fill="red"></circle>
</svg>

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

【问题讨论】:

    标签: javascript svg interactive


    【解决方案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>
    

    一些注意事项:

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

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

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

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

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

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

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

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

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

    6. 使用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"
         width="40px" height="40px" 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 width="100%" height="100%" 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" height="500" width="500" style="stroke-width:1; stroke:black; fill:white"></rect>
      <circle cx="250" cy="250" r="50" stroke="red" stroke-width="1" 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,它会这样做。
      猜你喜欢
      • 2010-10-13
      • 1970-01-01
      • 2018-04-06
      • 2016-09-11
      • 1970-01-01
      • 2012-08-08
      • 1970-01-01
      • 2017-08-04
      • 2018-04-29
      相关资源
      最近更新 更多