issue152:inkscape
Différences
Ci-dessous, les différences entre deux révisions de la page.
Les deux révisions précédentesRévision précédenteProchaine révision | Révision précédente | ||
issue152:inkscape [2020/01/06 08:51] – d52fr | issue152:inkscape [2020/01/08 15:36] (Version actuelle) – auntiee | ||
---|---|---|---|
Ligne 3: | Ligne 3: | ||
Once again we’ll start off with a very simple SVG file (top right).** | Once again we’ll start off with a very simple SVG file (top right).** | ||
- | La fois dernière, nous avons regardé l' | + | La dernière |
Une fois encore, nous commencerons avec un fichier SVG très simple (en haut à droite). | Une fois encore, nous commencerons avec un fichier SVG très simple (en haut à droite). | ||
Ligne 9: | Ligne 9: | ||
**As usual, we’ve got a viewbox of 100×100 units, but, this time, I’ve added a background < | **As usual, we’ve got a viewbox of 100×100 units, but, this time, I’ve added a background < | ||
- | Comme d' | + | Comme d' |
**The rectangle itself bears a little explanation. In an SVG document, the y-axis runs down the page, and the x-axis runs from left to right. So the origin – the 0,0 point in the image – is at the top-left of the page. The position of a < | **The rectangle itself bears a little explanation. In an SVG document, the y-axis runs down the page, and the x-axis runs from left to right. So the origin – the 0,0 point in the image – is at the top-left of the page. The position of a < | ||
- | Le rectangle lui-même | + | Le rectangle lui-même nécessite |
**There’s one other line in the file – a < | **There’s one other line in the file – a < | ||
Ligne 21: | Ligne 21: | ||
By loading the page in a web browser we can now easily confirm that we have a red square in the middle of a grey square, and that a message pops up from our JavaScript file to indicate that it’s being loaded correctly. Now we can move on to some real code.** | By loading the page in a web browser we can now easily confirm that we have a red square in the middle of a grey square, and that a message pops up from our JavaScript file to indicate that it’s being loaded correctly. Now we can move on to some real code.** | ||
- | Il y a une autre ligne dans le fichier | + | Il y a une autre ligne dans le fichier, une balise <script> qui fait référence à un document externe. Par souci de simplicité, |
alert(" | alert(" | ||
- | En chargeant la page dans un navigateur Web, nous pouvons facilement confirmer que nous avons un rectangle rouge au milieu d'un carré gris, et qu'un message apparaît depuis notre fichier | + | En chargeant la page dans un navigateur Web, nous pouvons facilement confirmer que nous avons un rectangle rouge au milieu d'un carré gris, et qu'un message apparaît depuis notre fichier |
**Based on the approach from part 90 of this series, we’re going to create a single function that updates the transform element for each frame that the browser renders. The function will receive a timestamp, and use that to determine how long the animation has been running, and therefore what values should be put into the transform element’s functions for that particular point in time. | **Based on the approach from part 90 of this series, we’re going to create a single function that updates the transform element for each frame that the browser renders. The function will receive a timestamp, and use that to determine how long the animation has been running, and therefore what values should be put into the transform element’s functions for that particular point in time. | ||
Ligne 33: | Ligne 33: | ||
Much of this looks similar to code we’ve seen previously, but there are enough differences to warrant a step-by-step walkthrough.** | Much of this looks similar to code we’ve seen previously, but there are enough differences to warrant a step-by-step walkthrough.** | ||
- | Basée sur l' | + | Basée sur l' |
- | Pour commencer, nous n' | + | Pour commencer, nous n' |
- | En grande partie, ça ressemble | + | En grande partie, ça ressemble |
**We start by declaring a global variable called “group”. Previously, we’ve used the “var” keyword to do this, but modern JS has mostly replaced that with “let” (for variables that will change), and “const” (for those that won’t). We’ve used “var” when working in the console as it won’t throw an error if you try to run the same line twice – as “const” would do. But, as we’re creating a separate JS file here, we’ll stick to convention. In this case, the “group” variable will eventually hold a reference to the <g> element, but as that’s not necessarily available as soon as the page loads, we’ll declare it using “let” and update the value later.** | **We start by declaring a global variable called “group”. Previously, we’ve used the “var” keyword to do this, but modern JS has mostly replaced that with “let” (for variables that will change), and “const” (for those that won’t). We’ve used “var” when working in the console as it won’t throw an error if you try to run the same line twice – as “const” would do. But, as we’re creating a separate JS file here, we’ll stick to convention. In this case, the “group” variable will eventually hold a reference to the <g> element, but as that’s not necessarily available as soon as the page loads, we’ll declare it using “let” and update the value later.** | ||
+ | |||
+ | Nous commençons par déclarer une variable globale appelée « group ». Précédemment, | ||
**Later doesn’t take long to arrive. The very next line of code causes our initialise() function to run just before the next frame is drawn, giving the browser time to render the content so that our <g> element actually exists in the document structure before we use it. The initialise() function itself does these things: | **Later doesn’t take long to arrive. The very next line of code causes our initialise() function to run just before the next frame is drawn, giving the browser time to render the content so that our <g> element actually exists in the document structure before we use it. The initialise() function itself does these things: | ||
Ligne 46: | Ligne 48: | ||
• The only other property we’re creating for now is the time it should take for the square to do one rotation, in seconds. | • The only other property we’re creating for now is the time it should take for the square to do one rotation, in seconds. | ||
• Finally, we have another call to getAnimationFrame() which will start the actual animation running.** | • Finally, we have another call to getAnimationFrame() which will start the actual animation running.** | ||
+ | |||
+ | Plus tard n' | ||
+ | ••Paramétrer notre variable globale « group » comme référence à l' | ||
+ | ••paramétrer quelques propriétés JS sur l' | ||
+ | ••La seule autre propriété que nous créons pour le moment est le temps en secondes que devrait mettre le carré pour faire une rotation. | ||
+ | ••Enfin, | ||
**Now we get to the animation code itself, in the form of the animate() function. We begin by getting a reference to the animation properties we set up previously, and storing it with a more convenient name. We can use “const” instead of “let” here as the value we assign doesn’t get changed within this function. | **Now we get to the animation code itself, in the form of the animate() function. We begin by getting a reference to the animation properties we set up previously, and storing it with a more convenient name. We can use “const” instead of “let” here as the value we assign doesn’t get changed within this function. | ||
The next group of lines just calculates the value, in degrees, that we need to rotate the square by. We get the amount of time the animation has been running for, by subtracting the initial timestamp from the current one, then divide the value by 1000 to convert from milliseconds to seconds. By dividing 360 by the desired rotation time we find the amount of rotation we need to perform every second; multiplying that value by the amount of time we’ve been running for gives a total value for the number of degrees to rotate by.** | The next group of lines just calculates the value, in degrees, that we need to rotate the square by. We get the amount of time the animation has been running for, by subtracting the initial timestamp from the current one, then divide the value by 1000 to convert from milliseconds to seconds. By dividing 360 by the desired rotation time we find the amount of rotation we need to perform every second; multiplying that value by the amount of time we’ve been running for gives a total value for the number of degrees to rotate by.** | ||
+ | |||
+ | Maintenant, nous avons le code de l' | ||
+ | |||
+ | Le groupe de lignes suivant calcule simplement la valeur, en degrés, de la rotation que nous devons appliquer au carré. Nous récupérons le durée depuis le lancement de l' | ||
**After the first rotation has completed, the calculated value will be larger than 360. That’s not actually a problem – the browser will happily do the right thing for you in this case – but I prefer to be a little explicit about what’s happening. That explains the last line of this block, where we use the modulus operator (%) to get the value that remains after dividing the total angle by 360. This has the effect of normalising the rotation angle so it never goes above 360, which can make it easier to see what’s happening if you need to log the value out, or if you view it live in the developer tools.** | **After the first rotation has completed, the calculated value will be larger than 360. That’s not actually a problem – the browser will happily do the right thing for you in this case – but I prefer to be a little explicit about what’s happening. That explains the last line of this block, where we use the modulus operator (%) to get the value that remains after dividing the total angle by 360. This has the effect of normalising the rotation angle so it never goes above 360, which can make it easier to see what’s happening if you need to log the value out, or if you view it live in the developer tools.** | ||
+ | |||
+ | Une fois le premier tour terminé, la valeur calculée sera plus grande que 360. Ce n'est pas vraiment un problème ; dans ce cas, le navigateur sera heureux de faire les choses correctement à votre place, mais je préfère être un peu explicite sur ce qui se passe. Cela explique la dernière ligne du bloc, où nous utilisons l' | ||
**The penultimate line uses setAttribute() to update our transform attribute with a new value. The value itself is a template string, delimited by backticks (`...`). They’re not always as easy to spot in code as the more usual quotes and double-quotes, | **The penultimate line uses setAttribute() to update our transform attribute with a new value. The value itself is a template string, delimited by backticks (`...`). They’re not always as easy to spot in code as the more usual quotes and double-quotes, | ||
+ | |||
+ | L' | ||
**The last line simply queues up another call to the animate() function, as we’ve seen previously. Load the SVG file into a web browser and, if everything is correct, you should see the square spinning around in the middle of the page. Press F12 in the browser to open the developer tools, and select the tab labelled “Inspector” (Firefox) or “Elements” (Chrome/ | **The last line simply queues up another call to the animate() function, as we’ve seen previously. Load the SVG file into a web browser and, if everything is correct, you should see the square spinning around in the middle of the page. Press F12 in the browser to open the developer tools, and select the tab labelled “Inspector” (Firefox) or “Elements” (Chrome/ | ||
+ | |||
+ | La dernière ligne enchaîne simplement sur un autre appel à la fonction animate(), comme nous l' | ||
**Rotation is pretty straightforward because we have to deal with only an ever increasing number. If we exceed a full rotation then we either normalise the number, or let the browser do it for us. The other transform functions are a little more tricky: skewX and skewY expect a value between -90 and +90 (though the extreme ends of the range distort the object so much that they’re not very useful); translate can take any number, but there’s only a limited range that makes sense within the confines of our 100×100 viewbox; scale has a similar practical limit. For all these transform functions, therefore, we want to animate back and forth between two values. This means creating three properties for each thing we want to animate, for the lower limit, upper limit and duration. Here’s how the group.animProperties object might be extended to also include skewX, for example (note the addition of a comma after the rotationDuration property, as this is no longer the last item in the object). Shown top right.** | **Rotation is pretty straightforward because we have to deal with only an ever increasing number. If we exceed a full rotation then we either normalise the number, or let the browser do it for us. The other transform functions are a little more tricky: skewX and skewY expect a value between -90 and +90 (though the extreme ends of the range distort the object so much that they’re not very useful); translate can take any number, but there’s only a limited range that makes sense within the confines of our 100×100 viewbox; scale has a similar practical limit. For all these transform functions, therefore, we want to animate back and forth between two values. This means creating three properties for each thing we want to animate, for the lower limit, upper limit and duration. Here’s how the group.animProperties object might be extended to also include skewX, for example (note the addition of a comma after the rotationDuration property, as this is no longer the last item in the object). Shown top right.** | ||
+ | |||
+ | La rotation est simple, car nous n' | ||
**To go with the new property, we’ll also need an extra group of lines in the animation function, just after the corresponding lines for rotation, but before the call to setAttribute() (bottom left). | **To go with the new property, we’ll also need an extra group of lines in the animation function, just after the corresponding lines for rotation, but before the call to setAttribute() (bottom left). | ||
We start by assigning props.skewDuration to a local variable, for no other reason than it gets used a lot, so we’ve given it a more convenient name. The second line subtracts the minimum value property from the maximum, to give us the total amount of possible skew. We’ll use this to work out what the current skew amount should be at any given timestamp.** | We start by assigning props.skewDuration to a local variable, for no other reason than it gets used a lot, so we’ve given it a more convenient name. The second line subtracts the minimum value property from the maximum, to give us the total amount of possible skew. We’ll use this to work out what the current skew amount should be at any given timestamp.** | ||
+ | |||
+ | Pour accompagner la nouvelle propriété, | ||
+ | |||
+ | Nous commençons par assigner props.skewDuration à une variable locale, pour la seule raison qu' | ||
**The third line calculates the “position” along the animation for the current timestamp. We do this by taking the running time (calculated earlier, in the previous block), dividing it by the duration for this animation, then taking the remainder. This gives us a value that runs from zero to the duration value, then jumps back to zero before ramping up again on each iteration. Rather than running from zero to “duration” it’s more useful if we adjust this value to be a decimal from 0 to 1, which is achieved by dividing by the total duration. | **The third line calculates the “position” along the animation for the current timestamp. We do this by taking the running time (calculated earlier, in the previous block), dividing it by the duration for this animation, then taking the remainder. This gives us a value that runs from zero to the duration value, then jumps back to zero before ramping up again on each iteration. Rather than running from zero to “duration” it’s more useful if we adjust this value to be a decimal from 0 to 1, which is achieved by dividing by the total duration. | ||
If we were to comment out the next few lines and jump to the last one, we would find that the animation cycles repeatedly from the minimum value to the maximum, jumping straight back to the minimum on each iteration. Plotting the values over time results in a “sawtooth” chart.** | If we were to comment out the next few lines and jump to the last one, we would find that the animation cycles repeatedly from the minimum value to the maximum, jumping straight back to the minimum on each iteration. Plotting the values over time results in a “sawtooth” chart.** | ||
+ | |||
+ | La troisième ligne calcule la « position » à l' | ||
+ | |||
+ | Si nous mettions en commentaires les quelques lignes qui suivent et passions à la dernière, nous verrions que l' | ||
**For our animation, however, we want the value to transition linearly both up and down, without the sudden jump between iterations. What we want is a triangle wave: | **For our animation, however, we want the value to transition linearly both up and down, without the sudden jump between iterations. What we want is a triangle wave: | ||
As you can see, on odd numbered iterations we want the animation to proceed as usual, but on the even numbered ones we want the position value to step downwards rather than upwards. In the code above this is done by creating a “skewXDirection” variable which holds the modulus of the current running time when divided by twice the duration. This value will ramp up from zero at the start of an odd iteration, through the duration value at the end of the odd iteration, continuing up to twice the duration value at the end of the subsequent even iteration. We’ve created another sawtooth wave, but this time running from zero to duration×2 over the course of two iterations.** | As you can see, on odd numbered iterations we want the animation to proceed as usual, but on the even numbered ones we want the position value to step downwards rather than upwards. In the code above this is done by creating a “skewXDirection” variable which holds the modulus of the current running time when divided by twice the duration. This value will ramp up from zero at the start of an odd iteration, through the duration value at the end of the odd iteration, continuing up to twice the duration value at the end of the subsequent even iteration. We’ve created another sawtooth wave, but this time running from zero to duration×2 over the course of two iterations.** | ||
+ | |||
+ | Pour notre animation, cependant, nous voulons une valeur qui varie linéairement vers le haut et vers le bas, sans saut brusque entre les itérations. Nous voulons un tracé triangulaire : | ||
+ | |||
+ | Comme vous le voyez, pour un nombre impair d' | ||
**The “if” statement that follows checks to see if this direction value is greater than the duration: if it is then we must be on an even cycle. In that case the “skewXPosition” variable (which, if you recall, ranges from 0 to 1) is subtracted from 1, so as the animation progresses the final position value first steps upwards, then steps downwards, before the cycle repeats in a triangle wave pattern. | **The “if” statement that follows checks to see if this direction value is greater than the duration: if it is then we must be on an even cycle. In that case the “skewXPosition” variable (which, if you recall, ranges from 0 to 1) is subtracted from 1, so as the animation progresses the final position value first steps upwards, then steps downwards, before the cycle repeats in a triangle wave pattern. | ||
Ligne 76: | Ligne 108: | ||
Phew! That was a lot to take in for a few lines of code. If you find it easier to follow, try adding some console.log() lines amongst the code so you can see how the values change in the developer tools.** | Phew! That was a lot to take in for a few lines of code. If you find it easier to follow, try adding some console.log() lines amongst the code so you can see how the values change in the developer tools.** | ||
+ | |||
+ | La déclaration « if » qui suit vérifie si cette valeur de direction est plus grande que la durée : si oui, alors nous sommes dans un cycle pair. Dans ce cas, la variable « skewXPosition » (qui, pour mémoire, varie de 0 à 1) est soustraite de 1, de sorte que l' | ||
+ | |||
+ | La dernière étape du calcul de la valeur réelle est pour multiplier la position actuelle dans le cycle par l' | ||
+ | |||
+ | Ouf ! Ça fait beaucoup à prendre en compte en si peu de lignes de code. Pour vous permettre de suivre plus facilement, essayez d' | ||
**With our final value calculated, the last step is to update the transform attribute to hold both the rotate() and the skewX() functions. Extend the previous template string to this: | **With our final value calculated, the last step is to update the transform attribute to hold both the rotate() and the skewX() functions. Extend the previous template string to this: | ||
Loading the file into the browser, you should see the square being skewed as it rotates. But you’ll also notice that our simple, constrained rotation in the middle of the screen has turned into a whirling dervish that swoops out of the bounds of our image before flying back in and then setting off into the distance once more. Next time we’ll discuss why this is happening, and finish this little animation by adding scaling and translation. In the meantime why not test your own understanding of this code by adding the necessary lines to make the shape also skew in the Y direction, at a different frequency to the skewX() effect.** | Loading the file into the browser, you should see the square being skewed as it rotates. But you’ll also notice that our simple, constrained rotation in the middle of the screen has turned into a whirling dervish that swoops out of the bounds of our image before flying back in and then setting off into the distance once more. Next time we’ll discuss why this is happening, and finish this little animation by adding scaling and translation. In the meantime why not test your own understanding of this code by adding the necessary lines to make the shape also skew in the Y direction, at a different frequency to the skewX() effect.** | ||
+ | |||
+ | Avec le calcul de notre valeur finale, la dernière étape est de mettre à jour l' | ||
+ | |||
+ | En chargeant le fichier dans le navigateur, vous devriez voir le carré qui se déforme tout en tournant. Mais vous devriez noter aussi que notre simple rotation contrainte au milieu de l' | ||
Ligne 85: | Ligne 127: | ||
ligne 7 // Durée d'une rotation de 360 degrés | ligne 7 // Durée d'une rotation de 360 degrés | ||
ligne 9 // Début du lancement de l' | ligne 9 // Début du lancement de l' | ||
+ | |||
+ | Traductions du cadre de la page 28 en haut à droite (à mettre en noir): | ||
+ | ligne 3 // Temps pour un tour complet (360°) | ||
+ | |||
issue152/inkscape.1578297086.txt.gz · Dernière modification : 2020/01/06 08:51 de d52fr