I wanted to make an SVG animation, but I got stuck in coordinate transformation. After searching for a long time, I finally found a clear tutorial.

Original address: css-tricks.com/transforms-…

The following article is based on this tutorial, and the final example comes from it.

preface

Like normal HTML elements, SVG elements can be transformed by a transform, but many of the details are quite different from normal HTML elements.

SVG elements:

,

, , , , etc. The “< SVG > element” in this article specifically refers to the element represented by the < SVG > tag. Too convoluted!! If you know what I mean

For SVG elements, a transform can be used in two ways, either as a transform property written inside an SVG element tag or as a transform in a CSS style.

Rect transform="rotate(45) scale(1.2)" width="100" height="100" fill="yellow" /> // Transform rect{transform: rotate(20deg) scale(1.2); }Copy the code

The difference between the two is that the transform in CSS style is not supported in older Versions of IE. If you want to be compatible with these browsers, you can select the transform attribute in the SVG element tag. Second, if it is the transform attribute written in the SVG element tag, all arguments to the transform function must be numbers, not units. By default, the Angle parameters in rotate and Skew are in deG.

The main difference between a normal HTML element and an SVG element in the Transform shape transformation is the difference in the local coordinate system. The origin of the local coordinate system determines the reference point of the transformation.

For ordinary HTML elements, the default value of the origin of the local coordinate system is the element’s center position in the X and y directions 50% 50% (only two-dimensional plane is considered here), that is, the element rotation, shift, scale and other operations are based on the element’s center position in the X and Y directions.

The SVG element, the origin of the local coordinate system, is 0 0 on the SVG canvas (the default is the upper-left corner of the < SVG > element) without any shape transformation to the SVG element.

How does the transform happen?

Translate, translation

Translation means that all the points on the element move the same distance in the same direction relative to the transformation reference point.

The image above shows the element translated by transform: Translate (295px,115px), with the normal HTML element on the left and the SVG element on the right.

Although the transformation reference point is different, the result is the same. In fact, it depends on the direction of the XY axis, the unit length.

The coordinate system for normal HTML elements and SVG elements is X positive to the right and Y positive to the down. Translate (295px,115px) : Move 295px to the right and 115px down. If you change the reference point to any point on the plane, you get the same result. But if the coordinate system moves right in the positive X direction and up in the positive Y direction, then translate(295px,115px) should translate 295px to the right and 115px up. The effect of unit length will be discussed later.

As mentioned earlier, for SVG elements, the transform can be written either in the CSS style or on the attributes of the SVG element tag.

//tx, ty is the X and Y direction translation, with the unit, such as transform: translate(20px) transform: translateX(tx) transform: translateX(tx) | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | |Copy the code
//SVG element attributes support syntax <rect... transform="translate(tx[ ty])" /> <rect ... Transform =" tx[, ty])" /> /*** 1, does not support translateX, translateY 2, the tx and ty only Numbers, cannot take the unit 3, tx, ty can be a blank space or comma between the transform = "translate (10. Transform ="translate(10 20)" ***/Copy the code

Rotate, rotate

The rotation of a figure on a plane is determined by two factors: the reference point and the Angle of rotation (the Angle plus or minus determines the direction of rotation).

Reference points are analogous to these pushpins: each card rotates around the pushpin that holds it, and the pushpin is the reference point of the card’s rotation transformation.

In this case the reference point is a point in the graph, or it could be a point outside the graph. Here’s an example:

A graph can choose any point in the plane as a reference point for rotation transformation. If you rotate it twice, you start at the same position, you rotate it at the same Angle, but you have a different reference point, you end up at a different position.

The image above is the result of a normal HTML element (left) and an SVG element (right) as transform: Rotate (45deg).

As shown above, the transformation reference point for a normal HTML element is the center of the element itself in the X and Y directions, whereas the reference point for an SVG element is the origin of the SVG canvas.

Transform: rotate(Angle) // Angle is the rotation Angle with units (deg, rad, turn, grad). Rotate (calc(.25turn-30deg)) // If Angle is positive, it is clockwise; if Angle is negative, it is anticlockwise.Copy the code
// The syntax supported by the element transform attribute <rect... Rotate ="rotate(Angle [x y])" /> /*** The rotate parameter is not only Angle, but also a pair of optional parameters x and y. Angle, x, and y can only be numbers and cannot contain units. Angle is measured by DEG. 3, Angle, x, y can be Spaces or commas ***/Copy the code

Optional arguments X, y from the SVG element’s transform attribute specify the reference point for this rotation. X and y must appear in pairs. If you write only one, it is invalid syntax.

Now that you can specify a reference point for rotation, what about SVG elements that rotate around their center?

<svg width="200" height="200"> <rect x="0" y="0" width="100" height="100" fill="yellow"/> </svg> <svg width="200" height="200"> <! -- Does not specify a reference point for this rotation, X ="0" y="0" width="100" height="100" fill="yellow" transform="rotate(45)"/> </ SVG > < SVG width="200" height="200"> <! -- specify the reference point of this rotation as the central coordinate of rect (50,50), X ="0" y="0" width="100" height="100" fill="yellow" transform=" 0" /> </ SVG >Copy the code

We finally got the first method to specify the transformation reference point, unfortunately it is one-time, it can only specify the rotation reference point.

When we do two rotations in a row, one clockwise, one counterclockwise, at the same Angle.

<svg width="200" height="200"> <rect x="0" y="0" width="100" height="100" fill="yellow" transform="rotate(45) rotate(-45)"/> </svg> <svg width="200" height="200"> <rect x="0" y="0" width="100" height="100" fill="yellow" The transform = "rotate (45,50,50) rotate (45)" / > < / SVG >Copy the code

You can see that the third rectangle does not revert to its original position, because the reference point of the first rotation is the center of rect, and the second rotation reference point is not specified, and the rotation reference point is the default upper left corner again.

Transform-origin can be used if you want every rotation to revolve around the center of the element without specifying (x,y) every time.

rect{ transform-origin: 50px 50px; // Center of rectangle}Copy the code
<svg width="200" height="200">
    <rect x="0" y="0" width="100" height="100" fill="yellow"/>
</svg>
<svg width="200" height="200">
    <rect x="0" y="0" width="100" height="100" fill="yellow" transform="rotate(45)"/>
</svg>
Copy the code

For SVG elements, transform-origin: right top, transform-origin: These are written for < SVG > elements (the blue border area above), such as transform-Origin: 50% 50%, which is actually the center of the blue border area. To locate the center of the yellow rectangle, write transform-origin: 50px 50px.

Scale – zoom

When you scale, what you change is the distance between all the points on the graph and the reference point of the transformation.

Derivation process (take the right side of the above figure as an example) : Select any point B on the figure and connect it with the transformation reference point A to form A line AB (more accurately described by vector AB), and the components of vector AB on the XY axis are (x,y). If the graph is scaled transform: scale(sx, xy), the component of vector AB on the xy axis will change to (sx.x,sy.y), and the original point B will be moved to the new point newB. Scaling is the result of all points on the graph being moved.

Transform: scale(sx, sy), sx>1,sy>1 If anyone in Sx,sy is less than 1, that will cause the graph to compress in that direction, and if anyone in Sx, SY is less than 0, that will cause the graph to reverse in that direction with respect to the transformation reference point. And we’re going to do the same thing, but you can do it again to make sense of it.

According to the principle of scaling, the result of scaling is affected by the transformation reference point. The image above is the result of a normal HTML element (left) and an SVG element (right) as transform: Scale (sx, SY).

Transform: scaleX(sx) transform: scaleY(sy) transform: // Scale (sx[, sy]) //scale (sx[, sy]) //scale (sx[, sy]) //scaleX(sx) and scaleY(sy) specify X and Y directions, respectively. ScaleX (sx) is equivalent to scale(sx,sy). 1),scaleY(sy) = scale(1,sy) //scale(sx[, sy])Copy the code
//SVG element attributes support syntax <rect... ScaleX, scaleY 2, tx, and ty can be Spaces or commas ***/. ScaleX, scaleY 2, tx, and TY can be Spaces or commas ***/Copy the code

For SVG elements, if you want to scale by the element’s own center point, you can specify transform-Origin in the CSS style as in the rotate transform above.

A more general and flexible method is the chain transformation.

Chain transformation

We know that a very important factor in transformation is the local coordinate system, whose XY axis direction, origin position and unit length directly affect the final result.

Is an element’s local coordinate system constant (specifically, XY orientation, origin, and unit length)?

In fact, translate, Scale, and rotate all change the element’s local coordinate system: Translate changes the origin of the local coordinate system, rotate changes the XY direction of the local coordinate system, and scale changes the unit length of the local coordinate system’s XY direction.

What we need to remember is that the reference point of each transformation is always the origin of the local coordinate system (if not specified by transform-Origin), the change in the (x, y) coordinate value is always measured in the XY axis (not horizontal or vertical), and the distance calculation is based on the unit length calculation. So with that rule, let’s analyze the chain transformation.

<rect x='65' y='65' width='150' height='80' transform='translate(140 105) scale(2 1.5) translate(-140-105)'/>Copy the code
  1. Determine the center point of the rectangle according to its x, y, width and height.

  2. Translate (140 105) Move the rectangle 140 Ux to the right and 105 Uy to the down (originally Ux=Uy=1), and transform the reference point to do the same, becoming (140, 105).

  3. As a relatively new reference point for transformation, scale(2 1.5) magnifies the rectangle by 2 times and 1.5 times on X and Y axes respectively, and the unit length of X and Y axes is also enlarged by 2 times and 1.5 times respectively, at which time Ux=2 and Uy=1.5

  4. Translate (-141-105) Move the rectangle 140 to the left and 105 to the upward. The unit length has changed, so the distance moved is actually 140 * 2,105 * 1.5. This is why the first translate(140 105) and the second translate(-140-105) do not move the same distance.

Translate, Scale, and rotate all change the element’s local coordinate system. This is the same for ordinary HTML elements as it is for SVG elements. After all, the only difference between them is the origin of the local coordinate system.

Take a normal HTML element:

div { 
    transform-origin: right bottom; 
    transform: 
        rotate(90deg) 
        translate(0, -100%) 
        rotate(90deg) 
        translate(0, 100%); 
}
Copy the code

  1. The initial transformation reference point is the lower right corner of the element
  2. rotate(90deg)The element rotates 90deg clockwise, while the local coordinate system rotates 90deg clockwise, X-axis positive downward, Y-axis positive left.
  3. translate(0, -100%)Move the width of the rectangle in the negative y-direction, that is, move it to the right, and transform the reference point to do the same.
  4. rotate(90deg)The element is rotated 90deg clockwise again, and the local coordinate system is rotated 90deg clockwise again, with the X-axis positive to the left and the Y-axis positive to the up.
  5. translate(0, 100%)You move the width of the rectangle in the positive Y direction, so you move it up.

A solution to transform SVG elements with their own center points

The solutions we know so far for SVG elements to transform to their own center point are:

1. Transform =”rotate(Angle [x y])” specifies the reference point for this rotation.

<rect x="0" y="0" width="100" height="100" transform=" 50,50 "/>Copy the code

2. Specify the transformation reference point using transform-origin.

rect{ transform-origin: 50px 50px; // Center of rectangle}Copy the code

3. Chain transformation

<rect x="0" y="0" width="100" height="100" transform="translate(50 50) rotate(45) translate(-50 -50)"/>
Copy the code

A more concise approach is to set the viewBox properties of SVG tags properly.

As we know, the origin of the SVG element’s local coordinate system, the transformation reference point, is 0 0 on the SVG canvas without any shape transformation on the SVG element, which defaults to the upper left corner of the < SVG > element.

We can divide the drawing and display of SVG into two parts: the canvas and the visible area.

A canvas can be thought of as a large two-dimensional (or even infinitely large) plane, starting with the upper-left corner of the < SVG > element, on which graphics can be positioned anywhere on the plane.

The visible region means that only the graphs within the region will be displayed, and those beyond the region will be truncated. By default, this is the area where the width (height and width attributes) of the < SVG > element is limited.

Understanding how SVG is drawn and displayed is crucial to understanding transform coordinate systems. For more details, see SVG from getting started to icon drawing and component encapsulation

Since the transformation reference point is the position 0 0 of the SVG canvas, if the center point of the graph overlaps the position 0 0 of the SVG canvas, then the graph is transformed at its own center point.

For a rectangle 150 by 80, set its upper-left coordinate to (-75, -40) so that its center point coincides with the origin of the SVG canvas.

<rect x='-75' y='-40' width='150' height='80'>
Copy the code

But in this case, the part of the graph with negative coordinates is clipped out of the visible area.

<svg>
   <rect x='-75' y='-40' width='150' height='80' fill="orange"></rect>
</svg>
<svg>
   <rect x='0' y='0' width='150' height='80' fill="orange"></rect>
</svg>
Copy the code

At this point, you need to set the viewBox property of the SVG tag to set the “new visible region”. The principles of SVG are explained in great detail, from getting started with SVG to icon drawing and component encapsulation.

<svg viewBox='-140 -105 280 210' height="200" width="300">
   <rect x='-75' y='-40' width='150' height='80' fill="orange"></rect>
</svg>
Copy the code

Set the upper-left coordinates (-140,-105), go 280 to the right, and go 210 down to the “new visible region”. The graph drawn in this region will be fully displayed, and the rectangle is in this region. Also, the center of the visible area is at the origin of the SVG canvas, so the rectangle is completely centered.

Here is an example of rotation:

<svg viewBox='-140 -105 650 350'> 
    <rect x='-75' y='-40' width='150' height='80' transform='rotate(45)'/> 
</svg>
Copy the code

Integrated case

The following demo will move the origin of the SVG canvas to the center of the visible area by setting the viewBox property of the SVG tag, and all SVG elements will be transformed around the center.

End result:

The source code is as follows:

<! DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <meta http-equiv="X-UA-Compatible" content="IE=edge"> <meta < span style> body{text-align: left; text-align: center; center; } svg{ background: #333; } @keyframes ani-1 { 0% { transform: scale(0); } 25% { transform: scale(1); } 50% { transform: rotate(120deg); } 75%, 100% { transform: rotate(120deg) translate(13em) scale(.2); } } @keyframes ani-2 { 0% { transform: scale(0); } 25% { transform: scale(1); } 50% { transform: rotate(240deg); } 75%, 100% { transform: rotate(240deg) translate(13em) scale(.2); } } @keyframes ani-3 { 0% { transform: scale(0); } 25% { transform: scale(1); } 50% { transform: rotate(360deg); } 75%, 100% { transform: rotate(360deg) translate(13em) scale(.2); } } use:nth-of-type(1){ fill: hsl(120, 100%, 80%); animation: ani-1 4s linear infinite; } use:nth-of-type(2){ fill: hsl(240, 100%, 80%); animation: ani-2 4s linear infinite; } use:nth-of-type(3){ fill: hsl(360, 100%, 80%); animation: ani-3 4s linear infinite; } </style> </head> <body> <svg viewBox='-512 -512 1024 1024' height="300" width="300"> <defs> <polygon id='star' Points = '250, 0, 0 (64-64 (0250-250-64-64, 64-250, 64' / > < / defs > < use href = '# star' / > < use href = '# star' / > < the use href='#star'/> </svg> </body> </html>Copy the code

Related articles:

A card game entry CSS3 3D transformation

SVG goes from getting started to icon drawing and component encapsulation

CSS animation basics