web-dev-qa-db-fra.com

Inclure JavaScript dans SVG

J'essaie de créer un code SVG interactif avec JavaScript, en incorporant le JavaScript dans le SVG. Je ne sais pas si c'est la bonne façon de procéder:

<?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>

Il est censé avoir une balle qui bouge avec wasd, mais la balle ne bouge pas. Qu'est-ce que je fais mal?

27
mortalc

Voici une version de travail telle que je l'écrirais:

<!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>

Quelques notes:

  1. Ne récupérez pas la référence au cercle encore et encore. Rendre votre code SEC le rend plus robuste, moins de frappe et (dans ce cas) plus rapide à exécuter.

    Modifier : Si vous ne pouvez pas comprendre comment faire cela étant donné mon code ci-dessus, postez tout code qui ne fonctionne pas pour vous.

  2. Ne vous fiez pas à un objet global event; c'est vieux IE non-sens. Utilisez l'objet d'événement passé à votre gestionnaire d'événements.

    Edit : Si vous référencez event dans votre code sans paramètre ou variable locale de ce nom, vous supposez qu'il y aura un ensemble d'objets global event. Au lieu de cela, consultez le code que j'ai écrit pour vous, qui montre que le gestionnaire d'événements reçoit un objet event. En lui donnant un nom, tel que je lui ai donné le nom evt, vous recevez un objet événement spécifique à votre gestionnaire d'événements.

  3. Puisque vous modifiez les variables x et y, il n'est pas nécessaire de récupérer les attributs cx et cy à chaque pression de touche.

    Modifier : Dans votre code d'origine et la réponse que vous avez acceptée, vous avez déclaré var x En dehors de votre gestionnaire d'événements, et vous avez x = ... Au début de votre gestionnaire d'événements, puis x++ Dans l'un des gestionnaires d'événements. Vous pouvez soit récupérer la valeur actuelle de x à chaque fois (comme vous l'avez fait) puis setAttribute(...,x+1), ou (comme je l'ai fait) vous ne pouvez récupérer que la valeur de l'attribut once avant les gestionnaires d'événements, puis supposez que cette valeur est correcte chaque fois que vous gérez l'événement clé.

  4. Ne placez pas vos gestionnaires d'événements JavaScript sur vos éléments, attachez-les par programmation.

    Edit : Dans votre balisage SVG, vous avez: <svg ... onkeypress="move()">. Mélanger votre comportement avec votre balisage est une très mauvaise idée en HTML et une mauvaise idée en SVG. Au lieu d'utiliser des attributs onfoo="..." Pour décrire ce qui doit se produire lorsqu'un événement se produit sur un élément, utilisez plutôt addEventListner() pour attacher les gestionnaires d'événements via du code, sans modifier votre balisage SVG.

  5. Il n'est pas nécessaire de contraindre les nombres à des chaînes avant de les définir comme attributs.

  6. Utilisez keydown et les codes d'événement ASCII que j'ai fournis ci-dessus au lieu de keypress et les nombres impairs que vous utilisiez si vous souhaitez que cela fonctionne dans tous les navigateurs.

    Edit : Vous vous êtes plaint dans n autre article que vous ne pouvez pas faire cela parce que vous voulez que le gestionnaire d'événements soit traité à plusieurs reprises comme clé est maintenu enfoncé. Notez que votre comportement souhaité est atteint avec mon exemple de code dans Chrome, Safari, Firefox et IE (Je n'ai pas Opera à tester). en d'autres termes, keydown fonctionne comme vous le vouliez, malgré la façon dont vous pensiez qu'il devrait se comporter.

Edit 2 : Si vous souhaitez inclure un bloc de script en haut de votre document, avant que tous les éléments aient nécessairement été créés, vous pouvez faire quelque chose comme le Suivant:

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

La fonction externe ne s'exécutera qu'une fois la page chargée, vous pouvez donc être sûr que les éléments existent pour les référencer.

61
Phrogz

vous pouvez ajouter le script js dans le code svg en déclarant la balise script dans la balise 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>
1

Cela fonctionne dans Chrome. Vous avez eu quelques erreurs, comme l'indexation de getElementsByTagName seulement parfois. De plus, le gros problème était que l'attribut onkeypress n'était pas contraignant.

<?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>
1
Josh Pearce