Outils pour utilisateurs

Outils du site


issue151:inkscape

Ceci est une ancienne révision du document !


Over the past few months, we’ve looked at ways to manipulate SVG in a web browser, using JavaScript, culminating in some simple animation. The code we used last time let us move an object horizontally on screen by manipulating its “x” attribute. By extension, we could also do the same with the “y” attribute to move it vertically. This time we’ll look at an alternative method of moving objects up, down, left and right, using an attribute that also opens up the ability to rotate or skew the element at the same time. The attribute in question is “transform”, and it crops up all the time in Inkscape documents, so let’s start by looking at how it’s used there.

Open Inkscape, with a fresh, blank document. Draw a simple square or rectangle – it doesn’t matter about the fill and stroke at this point, though it will be useful if you can actually see and interact with it. Now open the XML Editor dialog (Edit > XML Editor…).

By now, the right-hand side of this dialog should make some sense to you. You can see the usual collection of attributes that you would expect to find on a <rect>, together with their values – including the familiar “x” and “y” attributes.

Leaving the XML editor open, you should also open the Inkscape Preferences (Edit > Preferences…). Navigate to the Behaviour > Transforms screen, and ensure that the “Store transformation” option is set to “Optimised”.

You should now have these two dialogs open, together with your main Inkscape window. Arrange things so that you can easily get to the main canvas whilst seeing what happens in the XML editor. Select the rectangle in your document, then use the cursor keys to move it around a little. Notice that your “x” and “y” attributes in the XML editor change as you do so.

Now return to the Inkscape Preferences dialog and switch the “Store transformation” option to “Preserved”. Repeat the exercise of moving the rectangle around using the cursor keys, once again keeping an eye on the XML editor.

You should notice that, this time, the “x” and “y” attributes remain unchanged. Instead a new “transform” attribute is added, with a value that takes the form “translate(x,y)”. If you move your rectangle only horizontally, your “translate” will have only a single parameter – if it’s omitted entirely, the “y” parameter is considered to be zero.

That’s all very interesting, but what have we actually achieved? One important point is that we’ve now switched from using absolute coordinates to relative ones. Instead of setting absolute coordinates (“x” and “y” attributes), we’re combining an absolute starting point (the “x” and “y” attributes) together with a relative offset (the “x” and “y” parameters in the translate() function). Think back to our animation from last time: we had to keep track of the current “x” value and add our offset to it each time. By manipulating a “transform” attribute instead, we just have to set the offset directly, simplifying our code. It no longer matters what the coordinates were previously, we need to set only the translate() to the right values for the amount of time that has elapsed in our animation. It also leaves the original coordinates untouched, so moving the object back to its starting position is easier. This also makes our code more reusable: we can apply the same animation to various objects, each with their own “x” and “y” attributes, and therefore all separately positioned, despite sharing the same relative movement.

Another key use for the transform attribute is in combination with groups. The SVG <g> element doesn’t have its own “x” and “y” attributes, so moving a group of objects (as one) would require code to update the attributes of each and every element in the group on each step of the animation. By setting a transform attribute on the group, you can obtain the same effect with far less work.

So the transform attribute is a useful, if not essential, way of moving objects around in your drawing. But it offers more than that. Consider how you might incorporate rotation into your animation. If you’re animating a path – and you’re sufficiently mathematically astute – you could recalculate the coordinates of each node and handle in the path. But us mere mortals need an easier way to manage such tasks and the transform attribute offers that capability.

To see how it works, revert your rectangle back to a point where there’s no transform attribute showing in the XML editor, and change the “Store transformation” setting back to “Optimised”. With the selection tool active, click the rectangle a second time to switch to the rotate and skew handles. Use the corner arrows to rotate the rectangle and you should see a transform attribute appear, but this time with a value of “rotate®”, where “r” is the amount of rotation in degrees. By holding the Ctrl key, you can make the value jump between the steps defined in Inkscape’s preferences, or release the key for free rotation of your shape.

Notice that rotating the object not only adds a transform() with the rotation amount, but also changes the “x” and “y” values. Once again, set “Store transformation” to “Preserved”. Now the transform() function has three parameters: the rotation angle and the x and y coordinates of the center of rotation.

There are four other functions that can be used in the transform attribute: scale(x, y) skewX(a) skewY(a) matrix(a, b, c, d, e, f)

I’ve called these out separately because of the way Inkscape treats them. The scale() function simply increases or decreases the size of the object, stretching it if the x and y values are not the same. As with translate() the y parameter is optional.

skewX() and skewY() transform your element in the same way as the skew handles in the Inkscape UI. They each take a value, in degrees, that specifies the angle of skew.

These three functions map fairly obviously to the select tool in Inkscape, which can be used to stretch, scale and skew an object. So you might expect to see these functions appear in the XML editor as you manipulate your object. Instead you’ll get the matrix() function appearing.

Without going into too much detail, the matrix() function lets you supply a series of six numbers that are used to fill the first two rows in a 3×3 matrix. This is used via standard mathematical matrix multiplication to map the original coordinates of the object to the transformed coordinates. In non-mathematical terms, a single matrix can not only produce the same output as all the other transformation functions, but can also produce output that is equivalent to any combination or mixture of them. Want to skew, rotate, scale and translate all at once? A matrix() transform will do the trick.

Working out the six numbers that need to be passed to the matrix() function is not for the faint-hearted. They don’t correspond to simple values such as x, y and rotation. So although Inkscape likes to use the matrix() function internally, it’s probably not something you want to be manipulating in JavaScript. Fortunately there are a couple of ways to work with the individual functions, rather than being forced to combine everything into a single matrix().

The first is simply to wrap your objects in SVG groups (the <g> element), and apply a separate transformation to each one. Here’s how an SVG file might look (top right) if we took this approach to both skew and rotate a square:

When loaded into Inkscape the result looks like this:

Opening the XML editor shows that the transform on the outer group remains intact – it’s still a skewX() and doesn’t get automatically converted to a matrix() when loading it into Inkscape. As soon as you make a change via the GUI, however, the transform’s value will be replaced with a matrix(). If you just want to change the value in the existing function (eg. changing the angle of skew in this case), then you can make the modification in the XML editor. But remember that a <g> doesn’t have its own x and y attributes, so even something as trivial as moving the object slightly will mean Inkscape converts the attribute to a matrix() that combines the skew with the translate.

This is an important thing to be aware of. It’s easy to set up a file for animation with some nicely hand-coded transform attributes, then absent-mindedly open it in Inkscape to make a minor change, only to find that your hand-coded values have all been replaced with matrix() functions instead.

There is a second way to apply multiple transformations to an object: you just list them all in a single transform() element. Here’s a version of the previous file, but this time there’s no need for the <g> elements, since the transformation can be applied directly to the square (below).

Note that the transform attribute is now a list of transformations to apply. The white-space isn’t important: I’ve listed the functions one-per-line for clarity, but you could just put them onto a single line with a space character between them. When viewed in Inkscape, they appear in the XML editor on a single line, with every space and tab included between them, but none of the carriage returns:

Once again, there’s no real surprise with the appearance of the file when it’s loaded into Inkscape (see above).

Yet again, with this approach we face the same issue of Inkscape’s desire to convert the value into a single matrix(). This time we do have x and y attributes (since we’re working on the <rect> itself), so you might think that a “Store transformation” setting of “Optimised” might leave the transform untouched and just update the coordinates when you move the object around. Unfortunately, even in this case you’ll find that a matrix() comes along and tramples over everything, in addition to the x and y attributes changing.

It’s annoying that Inkscape doesn’t offer a third option beyond “Optimised” and “Preserved”. A “Verbose” option that stores transforms in a more human-friendly form. Instead of a composite matrix() function, you would get a list of separate functions in the attribute. Moving would add or update the translate(), rotating would add or update the rotate(), and so on. For anyone planning to manipulate their SVG file with code, the advantages of this approach would be huge.

As it stands, for most Inkscape users the internal details of how objects are moved, rotated, scaled and skewed is irrelevant. “Preserved” or “Optimised” has no bearing on how you work with elements in the GUI, or how the image is rendered in a web browser. If you do wish to alter the transform attribute using JavaScript, then there’s a slight advantage to “Preserved” – but only if the x and y attributes are set correctly in the first

issue151/inkscape.1575118120.txt.gz · Dernière modification : 2019/11/30 13:48 de auntiee