Outils pour utilisateurs

Outils du site


issue143:inkscape

Last time, we looked at some very basic JavaScript to alert or log a message when you click on an object in your drawing, or move the mouse over it. The one-line boxes in the object properties are okay for such short snippets of code, but you wouldn’t want to write anything too complex in there. Instead, Inkscape has a couple of other mechanisms for using larger amounts of code in your page: embedded scripts and external scripts. Both of these features live in the Document Properties dialog, so open that first (File > Document Properties, or CTRL-SHIFT-D), then switch to the “Scripting” tab. Within that area are two other tabs; in this article we’re going to look at the second one, “Embedded scripts”.

La dernière fois, nous avons regardé un peu de JavaScript très simple pour afficher une alerte ou enregistrer un message quand vous cliquez sur un objet de votre dessin, ou que vous passez la souris dessus. Les champs à une ligne des Propriétés de l'objet sont suffisantes pour de telles bribes de codes, mais vous ne pourriez pas écrire quelque chose de relativement complexe là-dedans. À la place, Inkscape dispose de quelques autres mécanismes pour utiliser des quantités de code plus importantes dans votre page : les scripts incorporés et les scripts externes.

Ces deux fonctionnalités vivent dans le dialogue des Propriétés du document ; aussi, ouvrons-le en premier (Fichier > Propriétés du document ou CTRL-MAJ-D), puis passons sur l'onglet « Programmation ». Dans cet espace se trouvent deux onglets : dans cet article, nous regarderons le second, « Programmes incorporés ».

In my opinion, there are a few issues with the UI in this dialog. You might think you can just go ahead and type some JavaScript into the “Content” section, but that won’t actually create anything in your file. Instead you must first either select an existing entry from the “Embedded script files” section, or create a new one using the “+” button and then select it. I don’t know why the Content section isn’t disabled until something is selected, nor why a newly created entry isn’t selected by default, but so long as you remember that anything typed into the bottom section will be lost unless there’s an entry selected in the top, you’ll be okay. Let’s create a new entry by clicking the “+” button, then select it and enter a little JavaScript into the bottom. We’ll just call the alert() function a couple of times at this point.

De mon point de vue, il y a quelques problèmes dans l'interface de ce dialogue. Vous pourriez penser que vous n'avez qu'à saisir tout de suite du JavaScript dans la section « Contenu », mais rien ne sera réellement créé dans votre fichier. En fait, vous devez d'abord, soit choisir une entrée existante dans la section « Fichiers de programmation incorporés », soit en créer une en cliquant sur le bouton « + », puis la sélectionner. Je ne sais pas pourquoi la section Contenu n'est pas désactivée jusqu'à ce qu'une sélection soit faite, ou pourquoi une nouvelle saisie n'est pas sélectionnée par défaut ; mais, tant que vous vous souvenez que tout de ce que vous saisissez dans la section du bas est perdu si aucune entrée n'est sélectionnée en haut, tout va bien.

Créons une nouvelle entrée en cliquant sur le bouton « + », puis sélectionnons-la et saisissons un peu de JavaScript en bas. Nous appellerons simplement la fonction alert() deux fois à ce stade.

Note that our alert() calls finish with semicolons, so the JavaScript interpreter knows where one statement ends and the next begins. Save your file, and open it directly in a web browser. You should immediately see two messages appear, even before the content of your document is rendered. JavaScript statements entered like this – outside of any function – are part of the global scope, and are executed as soon as the file is loaded. Now repeat the process to create a second embedded script file, with similar alert() calls, but the message changed to ‘Second embedded script…’. Save your file, and reload it in your web browser (F5). You should see four messages displayed in succession – but, if you read the details, you’ll notice that the ones from your second script are displayed first! This is something to be very careful with: the scripts appear in the XML file, and are therefore processed by the browser, in the order they appear in the list, not in the order you created them. In another UI faux-pas, however, it’s not possible to re-order the scripts in this dialog.

Notez que nos appels avec alert() finissent avec des points-virgules, de sorte que l’interpréteur de JavaScript sait où finit une déclaration et où commence la suivante. Enregistrez votre fichier et ouvrez-le directement dans un navigateur Web. Vous devriez voir immédiatement apparaître deux messages, avant même que le contenu de votre document ne soit rendu. Les déclarations du JavaScript entrées comme ça - en dehors de toute fonction - font partie du domaine global et sont exécutées dès le chargement du fichier.

Maintenant, répétons ce processus pour créer un second fichier de script incorporé, avec des appels alert() identiques, mais le message change pour « Second programme incorporé ». Sauvegardez votre fichier et rechargez-le dans votre navigateur Web (F5). Vous devriez voir quatre messages affichés l'un derrière l'autre - mais, si vous regardez en détail, vous noterez que ceux du second script sont affichés en premier ! C'est quelque chose sur lequel il vous faut rester vigilant : les scripts apparaissent dans le fichier XML et sont donc traités par le navigateur, dans l'ordre dans lequel ils apparaissent dans la liste, et non dans l'ordre où vous les avez créés. Cependant, autre faux pas de cette interface, il n'est pas possible de ré-ordonner les scripts dans ce dialogue.

One way around this problem is to just use a single embedded script, and manually reorder your lines in the Content box. Multiple scripts are all just concatenated together by the browser anyway, so whether you use a single script, or a hundred, it doesn’t matter from a JavaScript perspective. Note, however, that the Content box is a little short, and can’t be resized (another UI fail). If you want to put a lot of code into your file, then, being able to see only a tiny sliver of it at a time will make it rather difficult to work on. Usually, the ordering of the scripts isn’t too much of a problem, as JavaScript code is typically arranged into functions. The order in which the code executes then depends on the sequence in which the functions are called, not the order they appear in the file. This also goes some way to explaining why the fields in the Interactivity section of the Object Properties dialog allow only a single line of code: typically they have to make only a single call to execute a separate multi-line function. As an example of this, let’s use a function in an embedded script to change the fill color of an object when it’s clicked on.

Une façon de contourner ce problème est de n'utiliser qu'un seul script incorporé et de ré-ordonner manuellement les lignes dans la fenêtre Contenu. De toute façon, plusieurs scripts sont simplement mis bout à bout par le navigateur ; aussi, que vous utilisiez un seul script, ou une centaine, ça n'a aucune importance du point de vue du JavaScript. Cependant, notez que le champ Contenu est un peu réduit et que vous ne pouvez pas le redimensionner (autre défaut de l'interface). Si vous voulez mettre beaucoup de code dans votre fichier, le fait de ne pouvoir ensuite regarder qu'un petit morceau à la fois, rendra le travail dessus plutôt difficile.

Habituellement, l'ordonnancement des scripts n'est pas un gros problème, car le code du JavaScript est généralement arrangé par fonctions. L'ordre dans lequel le code s'exécute ensuite dépend de la séquence dans laquelle les fonctions sont appelées, et non de l'ordre de leur apparition dans le fichier. Ceci aussi explique pourquoi les champs de la section Interactivité du dialogue des Propriétés de l'objet n'autorisent qu'une seule ligne de code : habituellement, il n'y a besoin que d'un seul appel pour exécuter une fonction de plusieurs lignes. Comme exemple, utilisons une fonction dans un script incorporé pour changer la couleur de remplissage d'un objet quand on clique dessus.

In a new file, create a simple object – a square or circle – with a visible stroke and an obvious fill color. I’ve stuck with the red rounded rectangle I used last time. Now create a new embedded script file, with the following content: function change_to_blue(elem) { elem.style.fill = 'blue'; } We’ve created a function named change_to_blue() which takes a single parameter that we’ve chosen to call “elem” as it represents a single XML element in your file. This parameter will be a reference to the object you click on, and the body of the function just sets the fill color on that object to “blue” (a valid CSS color name). By putting our fill change inside a function we prevent it from running as soon as the page is loaded. Instead we have to explicitly call it from somewhere else in our file. That somewhere else is the “onclick” field of the Object Properties dialog, which I covered last time. To call our function, we simply have to invoke it by name, but we also need to pass a reference to the object you clicked on. JavaScript has a keyword, “this”, which means different things in different contexts – but in the case of a simple event handler like this, it gives us the reference we need. Therefore the line to put into the onclick field is this: change_to_blue(this); Save and reload your file, then click on your red object. It should turn to blue. See, interactive SVG isn’t so tricky after all!

Dans un nouveau fichier, créez un simple objet - un rectangle ou un cercle - avec un contour visible et, évidemment, un remplissage coloré. J'en suis resté au rectangle rouge à angles arrondis que j'ai utilisé la dernière fois. Maintenant, créez un nouveau fichier de script incorporé, avec le contenu suivant :

function change_to_blue(elem) {

elem.style.fill = 'blue';

}

Nous avons créé une fonction nommée change_to_blue() (changer en bleu) qui prend un seul paramètre que nous avons choisi d'appeler « elem » car il représente un seul élément XML dans votre fichier. Ce paramètre sera une référence à l'objet sur lequel vous cliquez et le corps règle simplement la couleur de remplissage de l'objet en « blue » (bleu) (un nom de couleur valide du CSS). En mettant notre changement de remplissage dans une fonction, nous l'empêchons d'être lancé dès que la page est chargée. Au lieu de ça, nous devons explicitement l'appeler de quelque part ailleurs dans le fichier. Ce quelque part ailleurs est le champ « onclick » du dialogue des Propriétés de l'objet, que nous avons vu la dernière fois. Pour appeler notre fonction, nous avons juste à l'invoquer par son nom, mais devons aussi passer une référence à l'objet sur lequel nous cliquons. JavaScript a un mot-clé, « this », qui signifie différentes choses dans des contextes différents - mais, dans le cas du gestionnaire d'un événement simple comme celui-ci, il nous donne la référence dont nous avons besoin. Ainsi donc, la ligne à mettre dans le champ onclick est celle-ci :

change_to_blue(this);

Enregistrez et chargez votre fichier, puis cliquez sur l'objet rouge. Il devient bleu. Vous voyez, après tout, le SVG interactif n'est pas si compliqué !

Try creating more objects, each with a different fill color, but each with the same line in their onclick field. Notice that clicking each one changes the color of only that specific element, thanks to the “this” keyword. Rather than just set the color to blue, how about creating a toggle between two colors each time the object is clicked. The code’s pretty straightforward: we just test to see if the fill color is currently ‘blue’ and, if so, set it to ‘red’. Otherwise we explicitly set it to blue. Here’s the code: function change_to_blue(elem) { if (elem.style.fill === 'blue') { elem.style.fill = 'red'; } else { elem.style.fill = 'blue'; } } If you’re not familiar with JavaScript, be particularly aware of the ‘===’ in the ‘if’ statement: this triple equals means “are both the value and the type of the variable identical?” It’s a more robust check than double equals (“are the values effectively the same, even if the types are different”), and is not the same at all as a single equals, which is used for assigning a value to a variable, not for testing it.

Essayez d'autres objets, chacun avec une couleur différente, mais chacun avec la même ligne dans le champ onclick. Notez que seul l'objet cliqué change de couleur, grâce à ce mot-clé « this ».

Plutôt que de régler simplement la couleur sur bleu, créons une bascule entre deux couleurs à chaque fois que l'objet est cliqué. Le code est assez simple : nous testons simplement pour voir si la couleur actuelle est « blue » et, si c'est le cas, pour la passer en rouge. Autrement, nous la réglons explicitement en bleu. Voici le code :

function change_to_blue(elem) {

if (elem.style.fill === 'blue') {
  elem.style.fill = 'red';
} else {
  elem.style.fill = 'blue';
}

}

Si vous n'êtes pas à l'aise avec JavaScript, soyez particulièrement attentif au « === » à la déclaration « if » : ce triple signe égal signifie « la valeur et le type de la variable sont-ils tous les deux identiques ? ». C'est une vérification beaucoup plus forte que le double égal (« Les valeurs sont-elles effectivement les mêmes, même si les types sont différents ? ») et n'est pas du tout la même chose que le simple égal qui est utilisé pour affecter une valeur à une variable, pas pour la tester.

This new code is all well and good, but it would be better still if, instead of simply toggling between blue and red, we toggled between blue and whatever color the object previously had. To do this we need to store the old value of the fill color before we change it to blue, then use that stored value when we turn it back again. Fortunately for us, the “elem” reference that is passed in (“this” on the calling element) is a JavaScript ‘Object’ (not the same as an object you draw in Inkscape), which can hold additional custom properties. We’ll dynamically create a new property, called ‘previousFill’ to hold the value of the fill just before we change it. Our toggling code becomes this: function change_to_blue(elem) { if (elem.style.fill === 'blue') { elem.style.fill = elem.previousFill; } else { elem.previousFill = elem.style.fill; elem.style.fill = 'blue'; } } In the “else” section we store the old fill in our ‘previousFill’ property; in the “if” section we use that value instead of the string “red”. Strictly speaking, we should probably also rename the function to toggle_fill() or something similar – but that suggests we could toggle to a color other than blue, which the code doesn’t do at the moment.

Ce nouveau code est bien beau, mais il serait encore meilleur si, au lieu de basculer simplement entre le bleu et le rouge, nous basculions entre le bleu et la couleur quelconque que l'objet avait avant. Pour faire cela, nous avons besoin de stocker l'ancienne valeur de la couleur de remplissage de l'objet avant que nous la changions en bleu, puis d'utiliser cette valeur enregistrée quand nous y revenons. Heureusement pour nous, la référence « elem » qui est passée en entrée (« this » est l'élément d'appel) est un « Object » du JavaScript (pas le même qu'un objet que vous dessinez dans Inkscape), qui peut contenir des propriétés additionnelles personnalisées. Nous allons créer dynamiquement une nouvelle propriété, appelée « previousFill » pour contenir la valeur du remplissage avant de la changer. Notre code de va-et-vient devient ainsi :

function change_to_blue(elem) {

if (elem.style.fill === 'blue') {
  elem.style.fill = elem.previousFill;
} else {
  elem.previousFill = elem.style.fill;
  elem.style.fill = 'blue';
}

}

Dans la section « else », nous stockons le vieux remplissage dans notre propriété « previousFill » ; dans la section « if », nous utilisons cette valeur au lieu de la chaîne « red » (rouge). Strictement parlant, nous devrions probablement aussi renommer la fonction toggle_fill() ou quelque chose du genre - mais ceci suggère que nous pouvons basculer sur une autre couleur que le bleu, ce que le code ne permet pas pour le moment.

Let’s extend it a little further so that we can toggle to a different color. By taking an optional second parameter we can let the calling code determine what the toggle color should be, but still fall back to blue as a default. The toggling code becomes this: function toggle_fill(elem, color) { if (color === undefined) color = 'blue'; if (elem.style.fill === color) { elem.style.fill = elem.previousFill; } else { elem.previousFill = elem.style.fill; elem.style.fill = color; } } Note that we test the color variable to see if it’s the special value ‘undefined’. Note that this is a primitive type in JavaScript, like ‘Number’, ‘String’ or ‘Object’, so we’re testing to see if color is this special type, not testing to see if it’s a string containing the word “undefined”. That’s why there are no quotes around the word in the code.

Étendons-la un peu plus de sorte que nous puissions basculer vers une autre couleur. En prenant un second paramètre optionnel, nous pouvons laisser le code d'appel déterminer ce que devrait être la couleur après commutation, tout en revenant malgré tout au bleu comme couleur par défaut. Le code de va-et-vient devient celui-ci :

function toggle_fill(elem, color) {

if (color === undefined) color = 'blue';
if (elem.style.fill === color) {
  elem.style.fill = elem.previousFill;
} else {
  elem.previousFill = elem.style.fill;
  elem.style.fill = color;
}

}

Notez que nous testons la variable de la couleur pour voir si c'est la valeur spéciale « undefined » (indéfinie). Notez que celle-ci est un type de primitive de JavaScript, comme « Number », « String » ou « Object » ; aussi, nous testons pour voir si la couleur est de ce type spécial, et non pour voir s'il y a une chaîne contenant le mot « undefined ». C'est pourquoi il n'y a pas de guillemets autour de ce mot dans le code.

Whenever a parameter is missing in a function call, the corresponding value in the receiving function is given a value of ‘undefined’. By explicitly testing for this, we can therefore decide what to do if the parameter is omitted – in this case use a default value of ‘blue’ instead. There are various ways to handle missing and default parameters in JavaScript, but this particular syntax is clear, robust, and works even in older browsers. With this default value in place, the calling code can be any one of these examples: toggle_fill(this); toggle_fill(this, undefined); toggle_fill(this, 'yellow'); toggle_fill(this, 'red'); This toggle_fill() function can therefore work with just a single parameter – in which case ‘color’ is undefined and gets set to ‘blue’ – or with two parameters. If the second parameter is explicitly set to ‘undefined’ then it’s the same as using just one parameter; otherwise the value will be used to set the fill color. But see how already, with only a short function like this, we’ve exceeded the number of lines in the Content box in Inkscape. I hope you like coding through a letterbox!

Chaque fois qu'un paramètre manque dans un appel de fonction, la valeur correspondante reçue dans la fonction est une valeur « undefined ». En testant ceci explicitement, nous pouvons par conséquent décider ce qu'il faut faire si le paramètre est manquant - dans ce cas, utilisez une valeur par défaut, « blue » à la place. Il est possible de gérer les paramètres manquants et par défaut de plusieurs façons dans JavaScript, mais cette syntaxe particulière est claire, solide et fonctionne même avec les anciens navigateurs.

Cette valeur par défaut mise en place, la fonction d'appel peut être n'importe lequel de ces exemples :

toggle_fill(this); toggle_fill(this, undefined); toggle_fill(this, 'yellow'); toggle_fill(this, 'red');

Cette fonction toggle_fill() peut par conséquent fonctionner avec seulement un paramètre - dans ce cas, « color » est indéfinie et devient « blue » - ou avec deux paramètres. Si le second paramètre est explictement réglé sur « undefined », c'est alors identique à l'utilisation d'un seul paramètre ; autrement, la valeur sera utilisée comme valeur de la couleur de remplissage. Mais, voyez comment déjà, avec une fonction courte comme celle-ci, nous dépassons le nombre de lignes du champ Contenu d'Inkscape. J'espère que vous aimez coder dans une boîte à chaussures !

Although this function is a lot more flexible than our original creation, you should be aware that not all valid CSS colors will work in this type of code: rgb() values might get returned by the browser as hexadecimal strings, for example, or the it might change the case, either of which will stop the equality test from working. Code like this, which makes assumptions about data without testing those assumptions rigorously, is fragile and easily broken. But writing this code in a less fragile way isn’t easy, and is certainly outside the scope of this tutorial series. For now you can play around with the code just to get a feel for embedding JavaScript into your SVG files. Next time, however, I’ll show you how to use CSS classes, in conjunction with JavaScript, to make toggling fill colors (and other styles) far more robust.

Bien que cette fonction est beaucoup plus flexible que notre création de départ, vous devez être conscient que, dans ce type de code, certaines couleurs valides du CCS ne marchent pas : les valeurs rgb() pourraient être retournées par le navigateur comme des chaînes hexadécimales, par exemple, ou il pourrait avoir changé la casse, les deux entraînant le non-fonctionnement du test d'égalité. Du code comme celui-ci, qui fait des suppositions sur les données sans tester ces suppositions rigoureusement, est fragile et facilement mis hors d'usage. Mais l'écriture de ce code d'une façon moins fragile n'est pas aisée et c'est certainement hors du sujet de cette série de tutoriels. Pour le moment, vous pouvez jouer avec ce code pour vous faire une bonne idée du JavaScript incorporé dans vos fichiers SVG. La prochaine fois, je vous montrerai comment utiliser les classes du CSS, conjointement avec JavaScript, pour rendre le va-et-vient des couleurs de remplissage (et d'autres styles) nettement plus robuste.

issue143/inkscape.txt · Dernière modification : 2019/04/15 09:50 de auntiee