Outils pour utilisateurs

Outils du site


issue144:inkscape

This month, we’re going to look at using CSS classes with your SVG in order to simplify the JavaScript you have to write when you want to change the style of your objects interactively. Note that we’re still talking only about changes that can be achieved using CSS styles – fill and stroke colors, line thickness and similar. Changing other aspects of your objects – such as the shape of a path – can’t be done simply by using CSS classes. First of all, what is a CSS class? In short, it’s simply a way to group similar objects for styling purposes, by giving them all the same class name. Consider this super simple SVG file, consisting of one red rectangle (top right).

Ce mois-ci, nous regarderons l'utilisation des classes du CSS avec votre SVG de façon à simplifier le JavaScript que vous devez écrire quand vous voulez changer interactivement le style de vos objets. Notez que nous ne parlons toujours que des modifications qui peuvent être réalisées en utilisant les styles du CSS - couleurs de remplissage et de contour, épaisseur des lignes et similaires. La modification d'autres aspects de vos objets, tels que la forme d'un chemin, ne peuvent pas être faits juste en utilisant des classes du CSS.

Avant tout, qu'est-ce qu'une classe du CSS ? En bref, c'est simplement une façon de grouper des objets similaires pour des besoins esthétiques, en leur donnant à tous le même nom de classe. Prenons ce fichier SVG super simple, consistant en un rectangle rouge (en haut à droite).

You’ve seen previously that we can remove the values in the ‘style’ attribute and put them into a <style> block elsewhere in the document (bottom right). The <style> block contains CSS declarations, consisting of a ‘CSS selector’, followed by a number of rules that will be applied to any elements in the file that match the selector. In this case, the selector is just the word ‘rect’, meaning that the rules applied here will match any <rect> elements in the file. Alternatively, we could use the id of an element, prefixed with a hash character, to make the rule apply to only the single element with that id: <style> #rect1 { fill: #ff0000; … We could also add a ‘class’ attribute to the element, then use the class name, prefixed with a dot, as the CSS selector (bottom left).

Vous avez vu précédemment que nous pouvons retirer les valeurs de l'attribut « style » et les mettre dans un bloc <style> quelque part ailleurs dans le document (en bas à droite).

Le bloc <style> contient des déclarations du CSS, faites d'un « CSS selector » (sélecteur du CSS), suivi par un certain nombre de règles qui seront appliquées à tous les éléments du fichier qui correspondent au sélecteur. Dans notre cas, le sélecteur est juste le mot « rect », signifiant que les règles appliquées ici le seront à tous les éléments <rect> du fichier. Autrement, vous pouvez utiliser l'identifiant (id) d'un élément, précédé d'un caractère #, pour que la règle ne s'applique qu'au seul élément ayant cet id :

<style> #rect1 {

fill: #ff0000;
...

Nous pouvons aussi ajouter un attribut « class » à l'élément, puis utiliser le nom de cette classe, préfixé d'un point (.), comme sélecteur du CSS (en bas à gauche).

In this case, I’ve used the class name ‘important-thing’, since the styling of a red fill with dark red background suggests this might be used to indicate important elements. But the class can be anything you like, provided you use only alphanumerics, underscores and hyphens. Note that you can’t use spaces in class names, for reasons that will become clear later. In this example file, we’ve not really gained much by using classes. The end result is the same whether we use a style attribute, or any of the three CSS selectors we’ve looked at, because there’s only one element in the file that can be affected. But what if we were to add a second element to our file: <circle id=“circle1” class=“important-thing” cx=“250” cy=“70” r=“50” />

Dans ce cas, j'ai utilisé le nom de classe « important-thing », car le style d'un filet rouge autour d'un fond rouge sombre suggère qu'il pourrait être utilisé pour indiquer des éléments importants. Mais la classe peut être tout ce que vous voulez, en se limitant à la seule utilisation de caractères alphanumériques, de traits de soulignement et de tirets. Notez que vous ne pouvez pas utiliser des espaces dans les noms de classe, pour des raisons qui s'éclairciront plus loin.

Dans ce fichier exemple, nous n'avons pas gagné grand-chose en utilisant les classes. Le résultat final est le même, que nous utilisions un attribut de style ou l'un des trois sélecteurs du CSS que nous avons vus, parce qu'il n'y a dans le fichier qu'un seul élément qui peut être affecté. Mais que se passe-t-il si nous ajoutons un second élément à notre fichier ? :

<circle

id="circle1"
class="important-thing"
cx="250"
cy="70"
r="50"

/>

This is a different type of object, so styling it through a <style> using the element selector ‘rect’ won’t work. It has a different id (as it must, because ids have to be unique in an XML document), so an id selector won’t work either. But classes don’t have to be unique, so we’ve given this circle the same class as our earlier rectangle. Here’s the result, two objects sharing a single style: It’s important to note that any styles you wish to set using a <style> block must not be present in the XML of the object itself. The inheritance rules of CSS dictate that styles set directly on elements usually take precedence over those set elsewhere. But this does give us the ability to override styles on individual elements. If we want a different fill color on the circle, we can simply set it in the style attribute, but still inherit the stroke width and color via the class: <circle id=“circle1” class=“important-thing” style=“fill: blue;” …

Ceci est un type d'objet différent ; aussi, traiter son esthétique via un <style> utilisant le sélecteur d'élément « rect » ne fonctionnera pas. Il a un id différent (il le doit, car les id doivent être uniques dans un document XML). Aussi un sélecteur d'id ne marchera pas non plus. Mais les classes n'ont pas à être uniques ; aussi, nous avons donné à ce cercle la même classe que notre rectangle précédent. Voici le résultat, les deux objets se partageant le même style :

Il est important de noter que tous les styles que vous souhaitez paramétrer en utilisant un bloc <style> ne doivent pas être présents dans le XML de l'objet lui-même. Les règles d'héritage du CSS dicte que les styles paramétrés directement sur les éléments ont, en général, la priorité sur ceux établis ailleurs. Mais ceci vous donne la possibilité d'outrepasser les styles sur des éléments particuliers. Si vous voulez une couleur de remplissage différente pour le cercle, vous pouvez simplement la paramétrer dans un attribut de style, mais hériter encore de la largeur et de la couleur du trait via la classe :

<circle

id="circle1"
class="important-thing"
style="fill: blue;"
...

You can, of course, have multiple classes defined in the <style> section of a file – but you can also apply multiple classes to a single object, by listing them all in the ‘class’ attribute, separated by spaces (the reason why class names themselves can’t contain spaces). Suppose we were to add another class, then modify the attribute for the rectangle: <style> …previous styles… .black-dashes { stroke: black; stroke-dasharray: 15,10; } </style> <svg …> <rect id=“rect1” class=“important-thing black-dashes” …

Vous pouvez, bien sûr, avoir plusieurs classes définies dans la section <style> du fichier ; mais vous pouvez aussi appliquer plusieurs classes à un seul objet, en les listant toutes dans l'attribut « class », séparées par des espaces (voici donc la raison pour laquelle les noms de classes ne peuvent pas avoir eux-mêmes des espaces). Supposez que nous ajoutions une autre classe puis modifiions l'attribut du rectangle :

<style>

...previous styles...

.black-dashes {

  stroke: black;
  stroke-dasharray: 15,10;
}

</style>

<svg …>

<rect
  id="rect1"
  class="important-thing black-dashes"
  ...

As you can see, the ‘important-thing’ class has been applied, giving the rectangle a red fill, but the ‘black-dashes’ class has overridden the stroke. It’s vital to understand that the ‘black-dashes’ overrides the other style because it’s declared later in the <style> block, not because of the order in which they’re put in the ‘class’ attribute. On the one hand, this is a limitation, in that you can’t trivially change the order in which the classes apply by modifying the attribute – but on the other hand it does mean that you can programmatically alter the content of the class attribute more easily, as you don’t have to worry about preserving the existing order.

Comme vous pouvez le voir, la classe « important-thing » a été utilisée, donnant au rectangle un remplissage rouge, mais la classe « black-dashes » a modifié le contour. C'est très important de comprendre que « black-dashes » remplace l'autre style, parce qu'elle a été déclarée plus tard dans le bloc <style>, et non à cause de l'ordre dans lequel ils ont été mis dans l'attribut « class ». D'un autre côté, c'est une limitation, en ce sens que vous ne pouvez pas bêtement changer l'ordre dans lequel les classes s'appliquent en modifiant l'attribut ; mais, en sens inverse, cela signifie que vous pouvez plus facilement modifier le contenu de l'attribut class par programmation, car vous n'avez pas à vous soucier de maintenir l'ordre existant.

Unfortunately, Inkscape doesn’t have any native support for creating and modifying CSS classes. Any edits you make to an element will be applied directly to its own ‘style’ attribute, or to other attributes directly on the element. Such changes won’t delete or change your <style> block, and won’t alter the ‘class’ attribute on the element, so at least Inkscape doesn’t completely destroy any manual edits you’ve applied. But, as we’ve seen, values set directly on an element will take priority over those applied via a class, so you can easily end up in a situation where your classes no longer appear to have any effect. For this reason, I recommend doing any work with classes in a text editor, rather than Inkscape, and making those changes as late in the design process as possible. Ideally you won’t have to re-open your document in Inkscape at all, but, if you do, take care not to change the styles of any elements that you expect to control using classes – or at least be prepared to re-edit the files in your text editor afterwards.

Malheureusement, Inkscape n'a pas de support natif pour créer et modifier les classes du CSS. Toutes les modifications que vous faites sur un élément seront directement appliquées sur son propre attribut « style », ou aux autres attributs, directement dans l'élément. De telles modifications n'effaceront ni ne modifieront votre bloc <style>, et n'altéreront pas l'attribut « class » de l'élément ; aussi, au final, Inkscape ne détruit pas complètement toutes les modifications manuelles que vous avez faites. Mais, comme nous l'avons vu, les valeurs mises directement sur un élément auront la priorité sur celles faites via une classe ; ainsi, vous pouvez facilement vous retrouver dans une situation où vos classes paraissent ne plus avoir aucun effet. Pour cette raison, je recommande de faire tous les travaux sur les classes dans un éditeur de texte, plutôt que dans Inkscape, et de faire ces modifications le plus tard possible dans la conception. Idéalement, vous ne devriez plus avoir à ré-ouvrir votre document dans Inkscape ; mais si vous le faites, prenez garde de ne modifier les styles d'aucun des éléments que vous espérez contrôler en utilisant des classes. Ou, du moins, soyez prêt à ré-éditer ensuite les fichiers dans votre éditeur de texte.

JavaScript offers a few ways to work with CSS classes, but by far the easiest is the ‘classList’ property. This has add(), remove(), toggle(), replace() and contains() methods that handle all the corner cases and error handling for you. You can add() a class without having to check if it’s already there. You can remove() a class and the code won’t throw an error if the class doesn’t exist. The toggle() method will add the class if it’s missing, or remove it if it’s present, which can simplify the code for basic on/off styling. You can use replace(), as the name suggests, to swap one class for another, and contains() does a search of the class attribute to tell you whether or not the name you supply is already present. Let’s finish up by using some of these methods in a new Inkscape drawing. First create a few objects whose classes you wish to alter: for this demo I’ll have three buttons to demonstrate some different approaches to the problem of toggling between two states.

JavaScript offre quelques façons de travailler avec les classes du CSS, mais la propriété « ClassList » est, de loin, la plus facile. Celle-ci ajoute les méthodes add(), remove(), toggle(), replace() et contains() qui manipulent tous les petits cas et la gestion des erreurs à votre place. Vous pouvez ajouter, « add() », une classe sans avoir à vérifier si elle est déjà présente. Vous pouvez retirer, « remove(), une classe et le code ne sortira pas une erreur si la classe n'existe pas. La méthode toggle() ajoutera la classe si elle est manquante, ou la supprimera si elle est présente, ce qui peut simplifer le code pour une esthétique simple visible/masquée. Vous pouvez utiliser replace(), pour passer d'une classe à l'autre, comme son nom le suggère, et contains() fait une recherche de l'attribut de classe pour vous dire si le nom que vous avez fourni est déjà présent.

Terminons en utilisant quelques-unes de ces méthodes dans un nouveau dessin d'Inkscape. D'abord, créez quelques objets dont vous souhaitez modifier les classes : pour cette démo, j'aurai trois boutons pour illustrer les différentes approches du problème de va-et-vient entre deux états.

After saving, it’s safest to close Inkscape before opening the SVG file in a text editor to make the CSS edits. In this case, we first want to add a couple of classes that will contain the styles we wish to switch between. For this demo, each one will contain only a fill color, and I’ll add a third class for the stroke. Because the stroke won’t be changing when we toggle, I don’t really need to add a class for it at all, but it helps to demonstrate that the classList methods work even when there’s more than one class applied. Here’s the code that gets added after the opening <svg> tag: <svg …> <style> .red { fill: #ff0000; } .blue { fill: #0000ff; } .black-stroke { stroke-width: 2; stroke: #000000; </style> …

Après avoir sauvegardé, par sécurité, il est préférable de fermer Inkscape avant d'ouvrir le fichier SVG dans un éditeur de texte pour faire les modifications du CSS. Dans ce cas, nous voulons d'abord ajouter quelques classes qui contiendront les styles entre lesquels nous voulons basculer. Pour cette démo, chacun ne contiendra qu'une couleur de remplissage, et j'ajouterai une troisième classe pour le contour. Parce que le contour ne changera pas quand je passerai de l'une à l'autre, je n'ai pas vraiment besoin de lui en ajouter une, mais ça aide à démontrer que les méthodes de classList fonctionnent toujours même quand on utilise plus d'une classe. Voici le code qui est ajouté après la balise ouvrante <svg> :

<svg …>

<style>
  .red { fill: #ff0000; }
  .blue { fill: #0000ff; }
  .black-stroke {
    stroke-width: 2;
    stroke: #000000;
</style>

Now we need to find each <rect> in the file and remove the fill, stroke and stroke-width properties from the ‘style’ attribute (or remove the corresponding attributes, if Inkscape has been configured to use presentation attributes rather than CSS styles). If you save the file at this point, and re-open it in Inkscape, you should see that the buttons now have an unset fill and stroke, with a default stroke width of 1. Quit Inkscape without making any changes. Back in the text editor, add a ‘class’ attribute to each <rect> so that they use the classes defined above for one of the fill colors, and the stroke. Here’s an abridged example of how one of them might look: <rect style=“display:inline; …” id=“rect10” class=“red black-stroke” … />

Maintenant, nous devons trouver tous les <rect> du fichier pour supprimer de l'attribut style le remplissage, le contour et les propriétés de largeur du trait (ou de supprimer les attributs correspondants si Inkscape a été configuré pour utiliser des attributs de présentation plutôt que les styles du CSS). Si vous sauvegardez le fichier à ce moment-là et que vous le rouvrez dans Inkscape, vous devriez voir que les boutons ont maintenant un remplissage et un contour indéfinis, avec une largeur du contour par défaut égale à 1. Quittez Inkscape sans faire aucune modification.

Revenez dans l'éditeur de texte et ajoutez un attribut « class » à chaque <rect> de sorte qu'ils utilisent les classes définies au-dessus pour chacune des couleurs de remplissage et pour le contour. Voici une vue abrégée de ce à quoi devrait ressembler l'un d'eux :

<rect

style="display:inline; ..."
id="rect10"
class="red black-stroke"
...

/>

At this point, Inkscape still claims the rectangles have an unset fill and stroke. But we can preview our work by opening the file in a web browser, which honours the class and style work we’ve done, and displays our buttons with a red background and thick black border. We could add the script directly in the SVG file with a text editor but, since this is an Inkscape column, that’s where we’re going to add our JavaScript code. Just take care not to change any styles when you open the file. With the file open in Inkscape, we’ll need to add an onclick handler to each element. Our first example is going to be a simple one-liner that just toggles the ‘blue’ class on and off. Because of the inheritance model in CSS, this will have the effect of overriding the ‘red’ class, so toggling will switch from red to blue and back. Right-click on the first button, select ‘Object Properties’ and expand the ‘Interactivity’ section of the dialog. In the ‘onclick’ field, type this: this.classList.toggle('blue');

À ce stade, Inkscape continue d'affirmer que les rectangles ont un remplissage et un contour indéfinis. Mais vous pouvez visualiser votre travail en ouvrant le fichier dans un navigateur Internet, qui fait honneur au travail que nous avons fait sur la classe et le style et affiche nos boutons avec un fond rouge et une épaisse bordure noire. Nous pourrions ajouter le script directement dans le fichier SVG avec un éditeur de texte, mais, comme c'est un article sur Inkscape, c'est là que nous allons pour ajouter notre code en JavaScript. Prenez juste soin de ne changer aucun style quand vous ouvrez le fichier.

Le fichier étant ouvert dans Inkscape, nous avons besoin d'ajouter un gestionnaire de « onclick » pour chaque élément. Notre premier exemple ne fera qu'une ligne, pour activer/désactiver la classe « blue ». Du fait du modèle d'héritage dans le CSS, ceci aura pour effet de remplacer la classe « red », de sorte que le va-et-vient se fera entre le bleu et le rouge. En faisant un clic droit sur le premier bouton, sélectionnez « Propriétés de l'objet » et agrandissez la section Interactivité du dialogue. Dans le champ « onclick », saisissez ceci :

this.classList.toggle('blue');

You can save the file, load it in a web browser, and confirm that it works, if you wish. For our second and third options we want more than just a single line of code, so we’ll create a pair of functions as embedded scripts. We’ll call them ‘toggle1’ and ‘toggle2’, and pass ‘this’ as a handle to the element that was clicked on. Add this line to the ‘onclick’ field in the object properties for the second button – and add an equivalent for ‘toggle2’ to the third button: toggle1(this); That’s the code to call the functions when the buttons are clicked, but now we need the functions themselves. Open the Document Properties and select the Scripting > Embedded Scripts tab. Click the ‘+’ button to add a new script, select it from the list, and put the toggle1 function in the Content area (top right).

Vous pouvez sauvegarder le fichier, le charger dans un navigateur Internet et confirmer que ça marche, si vous le voulez. Pour nos deuxième et troisième options, nous voulons plus qu'une simple ligne de code ; aussi, nous créons une paire de fonctions en tant que scripts incorporés. Nous les appellerons « toggle1 » et « toggle2 » et passerons « this » comme référence pour l'élément sur lequel nous avons cliqué. Pour le second bouton, ajoutez cette ligne dans le champ « onclick » dans les propriétés de l'objet et ajoutez l'équivalent pour « toggle2 » au troisième bouton.

toggle1(this);

C'est le code pour appeler les fonctions quand les boutons sont cliqués mais, maintenant, nous avons besoin des fonctions elles-mêmes. Ouvrez les Propriétés du document et dans Programmation, sélectionnez l'onglet Programmes incorporés. Cliquez sur le bouton « + » pour ajouter un nouveau script à la liste ; sélectionnez-le dans la liste et mettez la fonction toggle1 dans la zone Contenu (voir en haut à droite).

Here (bottom right) is the code for toggle2. You can either add this function after toggle1, or put it into a separate script section by clicking the ‘+’ and selecting the new entry. Save the file, open it in your browser, and you should be able to click each button to toggle it from red to blue. Look back over the code, and try to understand the differences between these three approaches. The first is the simplest, and will work in most cases where you just have to turn a class on or off from a single location in the code. The second is more useful if there are other bits of code that could interfere with the content of the class attribute, as the extra check to confirm if it currently contains the class or not makes it more robust. The third method is rarely used, but might be handy if you need to replace an entire class to avoid inheriting any properties from it. Usually I’d recommend reorganising your classes to avoid this problem, but the replace() method is worth knowing about in case that’s not an option.

Voici, à droite en bas, le code pour toggle2. Vous pouvez, soit ajouter cette fonction après toggle1, soit la mettre dans une section de script séparée en cliquant sur « + » et en sélectionnant une nouvelle entrée.

Sauvegardez le fichier, ouvrez-le dans votre navigateur et vous devriez pouvoir cliquer sur chaque bouton pour le faire passer alternativement en rouge ou en bleu.

Retournez regarder le code et essayez de comprendre les différences entre ces trois approches. La première est la plus simple et elle fonctionnera dans la plupart des cas où vous avez juste à activer/désactiver une classe à une seul endroit dans le code. La seconde est plus utile s'il y a d'autres bouts de code qui pourraient interférer avec le contenu de l'attribut class, car la vérification supplémentaire pour confirmer s'il contient actuellement la classe ou non, la rend plus robuste. La troisième méthode est rarement utilisée, mais elle peut s'avérer pratique si vous avez besoin de remplacer une classe entière pour éviter tout héritage de propriété venant d'elle. Habituellement, je recommanderais de réorganiser vos classes pour éviter ce problème, mais la méthode replace() vaut d'être connue dans le cas où on n'a pas le choix.

You may notice that clicking on the text, rather than the background, of the button does not cause the class toggle to occur. Furthermore the text is still selectable, which is not something you would usually want in a button. Next time we’ll take a look at these issues to see how we can make a click on one object affect a completely different one.

Vous pouvez noter qu'en cliquant sur le texte du bouton, plutôt que sur son fond, le basculement de la classe n'intervient pas. De plus, le texte est toujours sélectionnable, ce qui n'est pas ce que vous voulez habituellement sur un bouton. La prochaine fois, nous nous pencherons sur ces problèmes pour voir comment nous pouvons faire pour qu'un clic sur un objet affecte un tout autre objet.

issue144/inkscape.txt · Dernière modification : 2019/05/13 15:44 de andre_domenech