Outils pour utilisateurs

Outils du site


issue153:inkscape

Ceci est une ancienne révision du document !


Last time, we began work on an SVG file which uses JavaScript to animate the “transform” attribute of an object when loaded in a web browser. We’d got as far as animating the rotate() and skewX() functions, and you’d been left with the challenge of adding skewY() to the mix. Hopefully you worked out that this was mostly a case of copying the existing code, and replacing “X” with “Y”. Specifically: • Create three new properties in the animProperties object: skewYDuration, skewYMin, skewYMax. • Insert another block of code to calculate the current value of the skewY() function, given the supplied timestamp. This is literally a copy of the skewX code with the letter replaced throughout, resulting in a skewYAmount variable at the end. • Add another line to the setAttribute() call to include the skewY() function in the transform attribute, passing the value of the skewYAmount variable via the template string.

La dernière fois, nous avons commencé à travailler sur un fichier SVG qui utilise Javascript pour animer l'attribut «transform » d'un objet quand il était chargé dans un navigateur Web. Nous étions aller jusqu'à animer les fonctions rotate() et skewX() et je vous avait confié le challenge d’ajouter skewY() à cet ensemble. Avec un peu de chance, , vous avez trouvé qu'il s'agit, en gros, de copier le code de skewX() et de remplacer « X » par « Y ». Spécifiquement : ••Créer trois nouvelles propriétés dans l'objet animProperties : skewDuration, skewYMin, skewYMax. ••Insérer un autre bloc de code pour calculer la valur courante de la fonction skewY(), conmpte tenu de l'horodatage fourni. C'est littéralement une copie du code de skewX , en remplaçant tout du long la dernière lettre, avec pour résultat final, la variable skewYAmount. ••Ajouter une autre ligne à l'appel setAttribute() pour inclure la fonction skewY() dans l'attribut transform, en passant la valeur de al variable skewAmount via un chaîne modèle.

I also left you with something of a puzzle: with the addition of the skewX() function, the square doesn’t just rotate and skew in the middle of the screen as you might expect; instead it moves wildly in and out of the browser window as it rotates. Adding the skewY() function simply exacerbates the problem. Why does it do that? And how can we get the behaviour we expected? The reason is quite simple: whereas the rotate() function has an optional pair of parameters for setting the center of rotation, there’s no equivalent for the skew functions. Skewing takes place relative to a baseline, rather than a single point, but there’s no generic skew() function that lets you specify this baseline via two sets of coordinates. Instead, there are only two possible baselines available: the x-axis (via the skewX() function) and the y-axis (via the skewY() function). The graph below shows the effect of skewing along the x-axis for the same size of object positioned in a variety of places (bottom left)

Je vous avais aussi laissé avec une sorte de puzzle : avec l'ajout de la fonction skewY(), le carré ne tourne pas, ni ne se déforme, simplement au milieu de l'écran comme vous pourriez vous y attendre : à la place, il se déplace dans et largement en dehors de la fenêtre du navigateur pendant qu'il tourne. L'ajout de la fonction skewY() a exacerbé le problème. Pourquoi fait-il ça ? Et comment pouvons-nous obtenir le comportement attendu ?

La raison est assez simple : alors que la fonction rotate() a une paire de paramètres optionnels pour définir le centre de rotation, les fonctions skew n'ont pas d'équivalent. la déformation s'applique par rapport à une ligne de référence, plutôt qu'un point unique, mais il n' y a pas de fonction générique skew() qui vous permet de spécifier cette ligne de base via deux jeux de coordonnées. À la place, seulement deux lignes de référence possibles sont disponibles l'axe des x (via la fonction skewX()) et l'axe des y (via la fonction skewY()). Le graphique ci-dessous montre l'effet d'une déformation le long de l'axe x pour la même taille d'objet positionné dans différents endroits (en bas à gauche).

The red squares along the x-axis all skew “in-place”, resulting in the outline shapes displayed. The green and blue squares – colored to avoid confusion where they overlap – move to the left and right as a result of the skewing process. It doesn’t take much thought to realise that the amount of movement to the left and right is actually proportional to the distance from the x-axis, so even a small angle of skew can quickly move a shape by a large distance if it’s located far from the axis. And, of course, these rules also apply for the skewY() function, but rotated by 90°. You may recall that we did some deliberate manipulation of our object in order to position it in the middle of the screen (50, 50). Unfortunately, all that work means that skewing the shape also pushes it around. The problem is that we have two conflicting requirements: • We want the object centered at (50, 50) for display. • We need the object to be centered at (0¸ 0) in order to skew it.

Les carrés rouge le long de l'axe des x se déforme « sur place », entraînant les formes de contour qui sont affichées. Les carrés vert et bleu -colorés pour éviter toute confusion quand ils se superposent - se déplacent à droite et à gauche comme résultat du processus de déformation. Il n'y a pas vbesoin de réfléchir longtemps pour réaliser que la quantité de mouvement vers la droite et la gauche est vraiment proportionnel à la distance à l'axe des x ; ainsi, même un petit angle de déformation peut rapidement déplacer une forme sur une grande distance s'il est situé loin de l'axe des x. Et, bien sûr, ces règles s'appliquent aussi à la fonction skewY(), mais en tournant de 90°.

Vous devez vous rapeller que nous aonvs fairt quelques modifications délibérées à notre objet de façon à le positionner au milieu de l'écran (50, 50). Malheureusement, tout ce travail signifie que déformer la forme la pousse aussi quelque part. Le problème est que vnous avons deux nécessités contradictoires : ••Nous voulons que l'objet soit centré à (50, 50) pour l'affichage. ••Nous avons besoin que l'objet soit centré à (0, 0) de façon à le déformer.

There are a few ways to solve this conundrum: • Use a translate() function to move the object to (0, 0); then skew it; then use another translate() function to move it back again. • Change the x and y attributes of the object so that it starts at position (0, 0). After skewing, add a translate() function to move it to (50, 50). • Use a matrix() function rather than the skewX()/skewY() functions, as this can combine skewing and translating into a single call. I’m going to immediately dismiss the last option, as it requires far too much mathematics. But it does, perhaps, explain why Inkscape always uses matrix() rather than the named skew functions. The first option is probably the clearest in terms of what’s happening, but it results in the position of the object being calculated three times: once with the initial x and y values set to 50; once when it’s translated back to (0, 0) prior to the skew; then a final time when an inverse translation is applied to put the skewed version back into place.

Il y a quelques manières de résoudre ce casse-tête : ••Utiliser la fonction translate() pour déplacer l'objet à (0, 0° ; puis le déformer ; puis utiliser une autre fonction translate() pourle déplacer à (50, 50). ••Changer les attributs x et y de l'objet de sorte qu'il commence à la position (0, 0). Après la déformation, ajouter une fonction translate() pour le déplacer à (50, 50). ••Utiliser une fonction matrix() plutôt que les focntions skewX() et skewY(), car elle combine la déformationet la translation en un seul appel.

The best compromise for this project is the second approach. Simply changing the x and y values in the SVG, however, means that the source file no longer holds the “true” values for the coordinates, so if the JavaScript fails to load for some reason the square will be positioned at the top left, rather than in the middle of the screen. A slight modification to this approach – and the one we’ll take here – is to leave the SVG file untouched, but change the x and y values to zero at the start of the JS file. That way, if the script fails to load you still get a stationary fallback image with the square in the right place, but if everything loads correctly, the JS immediately changes the object’s coordinates to make for less work in the rest of the code. The first step is therefore to modify the start of the initialise() function to get a handle to the <rect> inside the group, and reset the attributes ( shown top right). We use group.querySelector() to get the rectangle as it limits the search to descendants of the group object (compared with document.querySelector() which would search the whole document), and it makes it easy to replace the argument with an ID or class selector when working with a more complex drawing, or with a different element selector if we change the content of the group to be a different type of object (e.g. a <path> or an <image> instead of the <rect>). Once we’ve got a handle to the element, it’s back to our old friend setAttribute() to set the new values we want.

Reloading the page at this point shows that, if anything, we’ve made the problem worse! Now the square is rotating over an even wider range than before, spending most of its time out of bounds. The reason for this is also a simple one: remember that we used the three parameter version of the rotate() transformation function, so even though our object is centered at (0, 0) it’s still rotating around (50, 50). Now we can pare that function down to the single value version, and the line where we set the value of the transform attribute becomes this (bottom right). Reload the page and we’ve got what we expected: a square that rotates and skews in-place at the top-left of the screen. Our last step is to move it back to (50, 50) with an extra translate() step inserted to the start of the list, whose values are hard-coded (next page, top right).

Note that the transformations are actually applied in reverse order: first the skewY(), then skewX(), then rotate() then finally translate(). When all we had was rotate and skew functions the order made little difference, but adding the translate() makes a huge difference. Put it at the end of the list, and we’re back to the same problem with the square zooming around as it skews. At the start of the list, however, we’ve got a nicely controlled square, rotating and skewing whilst never leaving the middle of the screen. To complete our set of transform functions, let’s add a scale(), so that our square also grows and shrinks. This function can take one or two parameters to indicate the scale factor: if only one is provided then the object is scaled equally in both the x and y directions; if you wish to scale the two directions differently, then you have to provide two parameters.

Note, however, that there’s no parameter for the center point of the scaling operation. As with the skew functions, your object has to be positioned with its center at (0, 0) if you don’t want it to move as well as change size. Since we’ve already handled this problem for skewing, we just need to ensure that our scale() function is put into the transform attribute after the translate(), to ensure that the scale operation is performed before the translation. To make our animation more interesting we’re going to animate the change in x and y scale separately, over different time periods but within the same range of 0.1 (one tenth of the width or height) to 3.0 (treble the width or height). We’ll encompass these parameters as two more sets of properties in the group.animProperties object (middle right).

Like the skew functions we also want to animate from the minimum to the maximum, then back again – as opposed to continuously going in one direction as we did with rotate(). We therefore need a couple of blocks of code to calculate the relevant value at any given time point, changing direction after each period. Below is the code for the x-axis scaling – compare it to the equivalent block for skewX() from last month and you should be able to reproduce code for the y-axis scaling yourself. And, of course, we need to add our scale() function and two new parameters to the transform attribute (top right). At last we have our object rotating, skewing and scaling, all while centered in the browser window – though a static screenshot doesn’t really do it justice. I’m going to finish this month with a couple of exercises for you to try, which build on the animation we’ve created over these previous few articles:

• Our final transform attribute has a fixed translate() function to position the square in the middle of the screen. Why not add another two sets of parameters to also animate the x and y position, causing the square to move around the window a little as well. Setting min and max values either side of 50 means you can replace the hard-coded coordinates in the existing translate(). Or you could have a negative min and positive max, then use the values in a second translate() function – but be careful of the ordering! • Try replacing the contents of the group with something else. It could be a more interesting single shape, such as a star or more faceted polygon, but it could also be any other Inkscape drawing – with multiple shapes and colors. Simply replacing the <rect> with an <image> element makes for an interesting effect, reminiscent of the kind of thing that required a Hollywood budget back in the 1980s.

The most important thing to remember is that this animation code – and the JS that we used previously in this series – are just examples to get you going. There’s no reason why you can’t create an animation that messes with the transform attribute whilst at the same time altering the fill and stroke, or manipulating the “d” attribute of a <path> in order to change the shape being drawn. With an understanding of how to change attributes and properties from JS you can create interactive or animated SVG images that go way beyond the frame-based limitations of an animated GIF. It’s a bit of a cliché, but the only real limit is your own imagination.

issue153/inkscape.1581021206.txt.gz · Dernière modification : 2020/02/06 21:33 de d52fr