Outils pour utilisateurs

Outils du site


issue137:inkscape

Last time, we looked at how to animate an SVG element using SMIL rather than CSS animations. We’ll continue with this topic for one more article, but my warnings from last time need to be repeated: although SMIL is (currently) more capable and flexible than the CSS option, Microsoft have chosen not to implement it in any of their browsers, leading to a slow but inexorable demise in support across all the browser vendors. For the time being, it still works in most browsers but, unfortunately, this is not a technology to bet on in the long term. I much prefer SMIL to CSS animations. I find the code easier to understand, and the fact that the animation data tend to live inside the elements they’re animating avoids any confusion as to which rules apply to which objects as your document becomes more complex.

La dernière fois, nous avons regardé comment animer un élément SVG en utilisant SMIL à la place des animations du CSS. Nous continuerons sur ce sujet pour un article de plus, mais mes avertissements du mois dernier doivent être répétés : bien que SMIL soit (actuellement) plus puissant et plus flexible que l'option du CSS, Microsoft a choisi de ne l'implémenter dans aucun de ses navigateurs, conduisant à un abandon lent mais inexorable de son support par tous les fournisseurs de navigateurs. À l'heure actuelle, il fonctionne encore dans la plupart des navigateurs mais, malheureusement, ce n'est pas une technologie sur laquelle parier à long terme.

Je préfère de beaucoup les animations SMIL à celles du CSS. Je trouve le code plus facile à comprendre et le fait que les données d'animation tendent à vivre dans les éléments qu'elles animent évite toute confusion concernant quelles règles s'appliquent à quels éléments, au fur et à mesure que votre document devient plus complexe.

With a complex document it becomes ever more likely that your animations won’t all have to run at the same time, but instead might run sequentially – or a mixture of both. Consider trying to animate something as sophisticated as a cartoon: being able to finely adjust the timing of each character’s movements is essential. CSS animations offer little to help you in this case, other than the brute-force option of adding a delay to your animations with this the sort of approach (shown right). Here we’re changing the fill color of a pair of rectangles (with IDs of “rect1” and “rect2”). The first changes from red to blue over 3 seconds. After a 3 second delay, the second rectangle changes from red to white over a 5 second period. Due to that 3 second delay, the animations occur sequentially.

Avec un document complexe, il devient toujours plus probable que vos animations ne devront pas toutes tourner en même temps mais plutôt en séquence - ou un mélange des deux. Imaginez que vous essayez d'animer quelque chose d'aussi sophistiqué qu'un dessin animé : être capable d'ajuster finement la séquence temporelle des mouvements de chaque personnage est essentiel. Les animations du CSS n'offrent pas grand chose pour vous aider dans ce cas, à part l'option brutale d'ajouter un retard dans vos animations, avec ceci comme sorte d'approche (montré à droite).

Ici, nous avons changé la couleur de remplissage d'une paire de rectangles (avec les repères « rect1 » et « rect2 »). Le premier passe du rouge au bleu en 3 secondes. Après un retard de 3 secondes, le second rectangle passe du rouge au blanc dans une période de 5 secondes. Du fait du retard de 3 secondes, les animations ont lieu séquentiellement.

Now what happens if you want to change the length of the first animation? You must also keep the delay for the second animation synchronised to the same value, otherwise it will either overlap the first, or occur some time after the first has finished. CSS does now support variables, so you can set the animation length once and reuse it as necessary, but the syntax is ugly and unwieldy (previous page, bottom right). Now imagine what your CSS would look like when you want to chain five animations, or ten – or a hundred! How much better it would be if you could just tell the second animation that it should start when the first one finishes. With SMIL, that’s exactly what you can do. The SMIL animation elements have an optional “begin” attribute which allows various ways of defining when the animation should start. At its simplest, you can just enter a delay, giving the same effect as the CSS animation above (shown top right).

Mais que se passe-t-il si vous voulez changer la durée de la première animation ? Vous devez aussi la maintenir synchronisée avec la même valeur de retard de la seconde animation. Autrement, soit elle va empiéter sur la première, soit arriver quelques instants après sa fin. CSS supporte maintenant les variables ; aussi, vous pouvez régler une fois la durée de l'animation et la réutiliser autant que nécessaire, mais la syntaxe est horrible et lourdingue (page précédente, en bas à droite).

Maintenant, imaginez à quoi ressemblerait votre CSS si vous vouliez chaîner cinq animations, ou dix, ou une centaine ! Ce serait beaucoup mieux si vous pouviez dire à la seconde animation de commencer quand la première finit. Avec SMIL, c'est exactement ce que vous pouvez faire. Les éléments de l'animation SMIL ont un attribut optionnel « begin » qui permet de définir de façon variée le début de l'animation. Dans sa forme la plus simple, il vous suffit de saisir un retard, donnant le même effet qu'avec l'animation CSS ci-dessus (montré en haut à droite).

But you can also define the beginning of an animation to be triggered by the end of another by using the ID of the other animation, followed by “.end” (bottom right). What happens if we want to change the length of the first animation now? No problem! Just modify the “dur” attribute and the second animation will still dutifully follow after the end of the first one. As well as the “.end” syntax you can also use “.begin” to link animations together so that they always start at the same time. You can optionally add an offset, such as “anim1.begin+2s” to make the animation begin 2 seconds after “anim1” starts, or even “anim1.end-0.5s” if you want your animation to begin half a second before the end of “anim1”.

Mais vous pouvez aussi décider que le début d'une animation sera déclenché par la fin d'une autre en utilisant l'identifiant (ID) de cette autre animation, suivi de .end (en bas, à droite).

Qu'arrive-t-il maintenant si vous changez la durée de la première animation ? Aucun problème ! Modifiez simplement l'attribut « dur » et la seconde animation suivra encore consciencieusement la fin de la première. Tout comme la syntaxe « .end », vous pouvez aussi utiliser « .begin » pour lier ensemble deux animations de sorte qu'elles démarrent toujours en même temps. Vous pouvez ajouter un décalage optionnel, tel que « anim1.begin+2s » pour que l'animation commence deux secondes après le début d'« anim1 », ou même « anim1.end-0.5s » si voulez que votre animation commence 0,5 secondes avant la fin d'« anim1 ».

SMIL allows animations to be repeated by adding a “repeatCount” or “repeatDur” attribute. For example, repeatCount=“5” would cause the animation to repeat five times, whilst repeatDur=“01:00” would cause it to repeat for one minute. When a repeating animation is used as the trigger for a second animation, this also allows an additional syntactic form to be used: begin=“anim1.repeat(2)” would cause the second animation to begin immediately after the second repeat of anim1 completes. Again, changes to the duration or start time of anim1 are automatically handled for you. In theory, these values can also be used for the “end” attribute, rather than for “begin”. That should allow you to specify that a second animation should finish three seconds after the end of the first animation, with the browser calculating when the animation should start in order to produce that result. Similarly, you should be able to specify values for “begin” and “end” with no duration set. In practice browsers fail to honor anything but a simple time-based “end” value.

SMIL permet la répétition des animations en ajoutant un attribut « repeatCount » ou « repeatDur ». Par exemple, repeatCount=“5” entraînerait cinq répétitions de l'animation, tandis que repeatDur=“01:00” provoquerait la répétition de l'animation pendant une minute. Quand une animation répétée est utilisée comme déclencheur d'une seconde animation, ceci permet aussi l'utilisation d'une seconde forme syntaxique : begin=“anim1.repeat(2)” entraînera le démarrage immédiat de la seconde animation après la fin de la deuxième répétition d'anim1. À nouveau, les modifications de la durée ou de l'instant de départ de anim1 sont automatiquement gérées à votre place.

En théorie, ces valeurs peuvent aussi être utilisées avec l’attribut « end », plutôt qu'avec « begin ». Ceci devrait vous permettre de spécifier que la seconde animation doit se terminer trois secondes après la fin de la première animation, le navigateur calculant quand l'animation devrait commencer pour produire ce résultat. De la même façon, vous devriez pouvoir spécifier des valeurs de « begin » et « end » sans durée établie. En pratique, les navigateurs n'arrivent à traiter qu'une simple valeur « end » basée sur le temps.

Speaking of features that don’t work in the browsers, the “begin” (and “end”) attributes can, theoretically, take several other forms. You should be able to use an ISO8601 format time or datetime value to trigger your animation at a particular absolute time, but I was unable to get that to work in a browser. Another option is an “accessKey” - i.e. a single key on the keyboard that, when pressed, would trigger the start of the animation. The presence of a demo for this on the Mozilla developer site suggests it used to work, but neither Firefox nor Chrome behaved as expected in my own tests. There’s one final option that does sort-of work, however: events. The following syntax, for example, will (in principle) trigger an animation when the rectangle is clicked on: <rect id=“rect1” fill=“#f00” …> <animate id=“anim1” attributeName=“fill” from=“#f00” to=“#00f” dur=“3s” fill=“freeze” begin=“rect1.click” /> </rect>

À propos des fonctionnalités qui ne marchent pas dans les navigateurs, les attributs « begin » (et « end ») peuvent théoriquement prendre plusieurs autres formes. Vous devriez pouvoir utiliser une valeur d'heure ou de date au format ISO8601 pour déclencher votre animation à un instant absolu particulier, mais j'ai été incapable de le faire fonctionner dans un navigateur. Une autre option est une « accessKey » (touche d'accès) - par exemple, une simple touche du clavier qui, quand elle est enfoncée, déclencherait le début de l'animation. La présence d'une démo de ceci sur le site des développeurs de Mozilla suggère que ça marchait dans le passé, mais ni Firefox, ni Chrome, ne se sont comportés comme attendu, dans mes propres tests.

Il y a cependant une dernière option qui fonctionne plus ou moins bien : les événements. La syntaxe suivante, par exemple, déclenchera (en principe) une animation quand on clique sur le rectangle :

<rect id=“rect1” fill=“#f00” …>

<animate id="anim1"
  attributeName="fill" 
  from="#f00" to="#00f" 
  dur="3s" fill="freeze"
  begin="rect1.click" />

</rect>

There are various events available, covering not only clicks but also mouse movements, scrolling and even changes to the structure of the document. Although the example above uses the parent element to trigger the animation, in practice you could use the ID of another element in the image – allowing a click on one element (styled as a Start button, perhaps) to trigger an animation on another. In practice this option does work, but only in situations when JavaScript would also be executed: when the SVG image is loaded directly, via an <object> tag, or is included inline with the HTML. It doesn’t work when SVG is loaded via an <img> element, which is a real shame as it could theoretically offer a safe way to produce interactive animations without the security risk of allowing JavaScript code to run.

De nombreux évènements sont disponibles, couvrant non seulement les clics de la souris mais aussi ses mouvements, le défilement d'une liste et même les modifications dans la structure d'un document. Bien que l'exemple plus haut utilise l'élément parent pour déclencher l'animation, en pratique, vous pourriez utiliser l'ID d'un autre élément de l'image, permettant qu'un clic sur un élément (représenté sous forme de bouton Marche, peut-être) déclenche une animation sur un autre.

En pratique, cette option fonctionne bien, mais seulement dans des situations où Javascript serait aussi exécuté : quand l'image SVG est chargée directement, via une balise <tag>, ou est incluse dans les lignes du HTML. Ça ne marche pas quand le SVG est chargé via un élément <img>, ce qui est vraiment dommage car ceci offre théoriquement une façon sûre de produire des animations interactives sans le risque sécuritaire d'autoriser le lancement du code Javascript.

And that pretty much sums up the failed promise of SMIL. If fully implemented it would have allowed the creation of complex animations triggered by mouse events or keypresses, with each component synchronised to other parts, all with a fairly simple declarative syntax that makes it safe to use via an <img> tag. Imagine a complex interactive animation, of the sort that you might see in a museum, but with the ability to be shared on forums or social media as easily as any other image. Before bidding farewell to SMIL entirely, I’m going to briefly discuss the last of the animation elements that are supported by SVG: <animateMotion>. This allows you to animate the position of an element along a path, either defined within the element itself or by reference to another path in the file. Consider this delightful evening scene (below).

Et voilà ce qui résume assez bien les promesses non tenues de SMIL. S'il avait été complètement implémenté, il aurait permis la création d'animations complexes déclenchées par des événements de la souris ou par des appuis sur les touches du clavier, chaque composant étant synchronisé avec d'autres parties, le tout avec une syntaxe déclarative assez simple qui rend sûre son utilisation avec la balise <img>. Imaginez une animation interactive complexe, dans le genre de celles que vous pourriez voir dans un musée, mais sans la possibilité de la partager sur des forums ou des réseaux sociaux aussi facilement que toute autre image.

Avant de dire un adieu définitif à SMIL, je vais vous présenter brièvement le dernier des éléments d'animation qui est supporté par SVG : <animateMotion>. Ceci vous permet d'animer la position d'un élément le long d'un chemin, défini, soit dans l'élément lui-même, soit par référence à un autre chemin dans le fichier. Regardez cette délicieuse scène nocturne (ci-dessous).

Note the orange path across the night sky, which I’ve given an ID of “animPath”. The yellow shooting star is made up of a group of objects, drawn so that the center of the star is at the top left of the document area (0,0 in SVG coordinates) – though I’ve moved it into the middle of the scene for this screenshot so that you can see it. By adding an <animateMotion> section inside the group, the shooting star will follow the orange path across the night sky (top right). The <animateMotion> element gets the usual animation attributes of “dur” and “fill” (and could have had “begin” and “end” if required), but has two attributes that are specific to this type of animation. The first is a “d” attribute which can contain path data of the same form that you would find in a <path> element. If present, this is used as the path along which the parent element will be animated.

Notez le chemin orange traversant la nuit étoilée, à qui j'ai donné l'ID “animPath”. L'étoile filante jaune est faite d'un groupe d'objets, dessinés de façon à ce que le centre de l'étoile soit à l'angle gauche en haut du document (0,0 en coordonnées SVG), bien que je l'aie déplacée au milieu de la scène dans cette copie d'écran pour que vous la voyiez. En ajoutant une section <animateMotion> dans le groupe, l'étoile filante suivra le chemin orange dans le ciel nocturne (en haut à droite).

L'élément <animateMotion> dispose des attributs usuels « dur » et « fill » (et pourrait avoir « begin » et « end », au besoin), mais il a deux attributs qui sont spécifiques à ce type d'animation. Le premier est l'attribut « d » qui peut contenir les données du chemin sous la même forme que celle que vous trouveriez dans un élément <path>. S'il est présent, il est utilisé comme le chemin le long du quel l'élément parent sera animé.

An alternative to directly including the path data in the <animateMotion> is to reference a separate path that is present elsewhere in the document. This is the approach I’ve taken here, by including an <mpath> (“motion path”) child element that refers to the ID of our animation path via the “href” attribute. Although modern browsers understand “href” as a native attribute in SVG, using the “xlink” namespace provides better compatibility with older software, so that’s what I’ve done here. The big advantage of using a linked path like this is that the path is an element that can be modified in Inkscape, whereas an embedded “d” attribute isn’t. The second attribute that is specific to <animateMotion> is “rotate”, which is an SVG addition which is not present in the base SMIL specification. This can take a number, in which case the object is rotated by that number of degrees, although a fixed rotation is probably better achieved using a “transform” attribute. More usefully, this attribute can take a value of “auto”, in which case the rotation of the element follows the shape of the path (there’s also an “auto-reverse” option which does the same, but rotates the animated element through 180° first). Here’s the effect of each option as the star descends on its path; notice particularly how rotate=“auto” has turned it to suit the descending curve of the line.

Une alternative à l'inclusion directe des données du chemin dans <animateMotion> est de faire référence à un chemin séparé qui est présent ailleurs dans le document. C'est cette approche que j'ai prise ici, en incluant un élément enfant <mpath> (« motion path » - chemin du mouvement) qui fait référence à l'ID de notre chemin d'animation via l'attribut « href ». Bien que les navigateurs modernes comprennent que « href » est un attribut natif de SVG, l'utilisation du nom d'espace « xlink » fournit une meilleure compatibilité avec les logiciels plus anciens, de sorte que c'est ce que j'ai fait ici. Le grand avantage d'utiliser un chemin lié comme ceci est que le chemin est un élément qui peut être modifié dans Inkscape, là où un attribut « d » ne peut pas l'être.

Le deuxième attribut spécifique à <animateMotion> est « rotate », qui est un ajout du SVG qui n'est pas présent dans la spécification de base de SMIL. Ceci peut être un chiffre ; dans ce cas, l'objet est tourné par ce nombre-là de degrés, bien qu'une rotation fixe est probablement bien mieux réalisée en utilisant l'attribut « transform ». Plus utilement, cet attribut peut prendre la valeur « auto » ; dans ce cas, la rotation de l'élément suit la forme du chemin (il y a aussi une option « auto-reverse » qui fait de même, mais en tournant d'abord l'élément animé de 180 °). Voici l'effet de chaque option quand l'étoile parcourt son chemin : notez particulièrement maintenant comment rotate=“auto” l'a fait tourner pour suivre la courbe descendante de la ligne.

You may be wondering about that orange path. The final step in designing an animation like this is typically to hide the animation path somehow. I usually move the path down in the z-stack behind everything else, or change its stroke color or opacity to make it transparent. Even when it’s transparent you can still get to it using Inkscape’s View ‣ Display Mode ‣ Outline option, if you do need to make some later changes. Although this simple example uses just a single curved path segment, the animation path can be as complex as you like with loops, twists, curves and sharp corners, so being able to tweak it graphically in Inkscape can be invaluable. One final thing to note is that although my test animation ran smoothly in both Chrome and Firefox when the SVG file was loaded directly, referencing it via an <img> tag in a web page resulted in a choppy animation in Firefox.

Vous pourriez vous poser des questions sur ce chemin orange. L'étape finale dans la conception d'une animation comme celle-ci est typiquement de masquer d'une façon ou d'une autre le chemin de l'animation. Habituellement, je déplace le chemin derrière autre chose dans l'ordre d'empilement, ou je modifie la couleur du trait ou l'opacité pour le rendre transparent. Même s'il est transparent, vous pouvez toujours l'atteindre en utilisant l'option d'Inkscape Affichage > Mode d'affichage > Contour, si vous avez vraiment besoin de faire des modifications ultérieurement. Bien que ce simple exemple utilise uniquement un seul segment courbe de chemin, l'animation peut être aussi complexe que vous le souhaitez, avec des boucles, des torsions, des courbes et des angles aigus ; aussi, la possibilité de l'ajuster graphiquement dans Inkscape est inestimable.

Une dernière chose à noter est que, bien que mon animation de test puisse fonctionner sans encombre dans Chrome comme dans Firefox, quand le fichier SVG est chargé directement, y faire référence via une balise <img> dans une page Web donne une animation agitée dans Firefox.

I’ll leave you with a little SMIL anecdote: back in 2011 I made use of SMIL for an Easter egg in one of my webcomics, to animate a UFO flying over the scene. The animation path itself is seemingly jerky and erratic, but digging into the file in Inkscape reveals that the path actually encodes a URL. Visiting that address shows a small demo of what SMIL can do: by using some JavaScript to dynamically add and modify SVG and SMIL elements, I wrote a simple Space Invaders style game that runs in the browser. JavaScript handles the game logic, with SMIL responsible for ensuring that the flying saucers move smoothly around the sky. For the time being, at least, it runs in all the major browsers, except Microsoft’s… These couple of articles have just provided a brief introduction to SMIL. With browser support waning, it’s unlikely to ever fulfill its early promise of allowing interactive animations in a way that can be safely used online anywhere a simple image is allowed. As is too often the case, it appears that corporate politics has killed a promising technology.

Je vous quitte avec une petite anecdote sur SMIL : en 2011, j'avais utilisé SMIL pour un œuf de Pâques dans une de mes bandes dessinées, pour animer un OVNI volant au-dessus de la scène. Le chemin d'animation lui-même paraissait saccadé et erratique, mais en fouillant dans son fichier dans Inkscape, il s'est révélé que le chemin encodait en fait une URL. Une visite à cette adresse montre une courte démo de ce que peut faire SMIL : en utilisant un peu de Javascript pour ajouter et modifier dynamiquement les éléments SVG et SMIL, j'avais écrit un simple jeu du style Space Invaders qui tournait dans le navigateur. Javascript gère la logique du jeu, et SMIL est doit s'assurer que les soucoupes volantes se déplacent sans accroc dans le ciel. À l'heure actuelle, au moins, ça fonctionne sur tous les navigateurs, à part ceux de Microsoft…

Ces deux articles ont tout simplement fourni une courte introduction à SMIL. Avec la disparition du support par les navigateurs, ses promesses initiales de permettre des animations interactives d'une manière qui puisse être utilisée en ligne en toute sécurité partout où une simple image est autorisée semblent irréalisables. Comme c'est trop souvent le cas, il apparaît que la politique d'un grand groupe a tué une technologie prometteuse.

issue137/inkscape.txt · Dernière modification : 2018/10/12 12:42 de auntiee