Rotating keyframe with static translation [duplicate] - css

After combing through the SVG specification, and guides such as this and this, I am still struggling to understand exactly how chaining transforms work.
Selected Relevant Quotes
When you apply the transform attribute to an SVG element, that element
gets a "copy" of the current user coordinate system in use.
And:
When transformations are chained, the most important thing to be aware
of is that, just like with HTML element transformations, each
transformation is applied to the coordinate system after that system
is transformed by the previous transformations.
And:
For example, if you’re going to apply a rotation to an element,
followed by a translation, the translation happens according to the
new coordinate system, not the inital non-rotated one.
And:
The sequence of transformations matter. The sequence the
transformation functions are specified inside the transform attribute
is the sequence they are applied to the shape.
Code
The first rectangle's current coordinate system is scaled, then rotated (note the order). The second rectangle's current coordinate system is rotated, then scaled.
svg {
border: 1px solid green;
}
<svg xmlns="http://www.w3.org/2000/svg">
<style>
rect#s1 {
fill: red;
transform: scale(2, 1) rotate(10deg);
}
</style>
<rect id="s1" x="" y="" width="100" height="100" />
</svg>
<svg xmlns="http://www.w3.org/2000/svg">
<style>
rect#s2 {
fill: blue;
transform: rotate(10deg) scale(2, 1);
}
</style>
<rect id="s2" x="" y="" width="100" height="100" />
</svg>
Question
We know that when we chain transforms, a copy is made of the current coordinate system in use for that element, then the transforms are applied in the order they are specified.
When we have a user coordinate system that is already scaled, and we apply a rotate to it, the rectangle is (as seen) effectively skewed (notice the changed angles). This does not happen if we do the two transforms the other way around (rotate, then scale).
Expert help on exactly how the scaled current coordinate system is rotated, would be deeply appreciated. I am trying to understand, from a technical (inner workings) angle, exactly why the skewing happens in the first rectangle.
Thank you.

To illustrate how it works let's consider an animation to show how the scaling effect change the rotation.
.red {
width:80px;
height:20px;
background:red;
margin:80px;
transform-origin:left center;
animation: rotate 2s linear infinite;
}
#keyframes rotate {
from{transform:rotate(0)}
to{transform:rotate(360deg)}
}
<div class="container">
<div class="red">
</div>
</div>
As you can see above, the rotation is creating a perfect circle shape.
Now let's scale the container and see the difference:
.red {
width:80px;
height:20px;
background:red;
margin:80px;
transform-origin:left center;
animation: rotate 5s linear infinite;
}
#keyframes rotate {
from{transform:rotate(0)}
to{transform:rotate(360deg)}
}
.container {
display:inline-block;
transform:scale(3,1);
transform-origin:left center;
}
<div class="container">
<div class="red">
</div>
</div>
Notice how we no more have a circle but it's an ellipse now. It's like we took the circle and we stertch it which is creating the skew effect inside our rectangle.
If we do the opposite effect and we start by having a scale effect and then we apply a rotation we won't have any skewing.
.red {
width:80px;
height:20px;
background:red;
margin:80px;
animation: rotate 2s linear infinite;
}
#keyframes rotate {
from{transform:scale(1,1)}
to{transform:scale(3,1)}
}
.container {
display:inline-block;
transform:rotate(30deg);
transform-origin:left center;
}
<div class="container">
<div class="red">
</div>
</div>
To explain it differently: Applying a rotation will keep the same ratio between both X and Y axis so you won't see any bad effect when doing scale later but scaling only one axis will break the ratio thus our shape we look bad when we try to apply a rotation.
You can check this link if you want more details about how transform are chained and how the matrix is caclulated: https://www.w3.org/TR/css-transforms-1/#transform-rendering. It's about HTML element but as said in the SVG specification it's the same.
Here is the relevant parts:
Transformations are cumulative. That is, elements establish their local coordinate system within the coordinate system of their parent.
From the perspective of the user, an element effectively accumulates all the transform properties of its ancestors as well as any local transform applied to it
Let's do some math in order to see the difference between both transformations. Let's consider matrix multiplication and since we are dealing with a 2D linear transformation we will do this on ℝ² for simplicity1.
For scale(2, 1) rotate(10deg) we will have
|2 0| |cos(10deg) -sin(10deg)| |2*cos(10deg) -2*sin(10deg) |
|0 1| x |sin(10deg) cos(10deg) | = |1*sin(10deg) 1*cos(10deg) |
Now if we apply this matrix to an (Xi,Yi) we will obtain (Xf,Yf) like below:
Xf = 2* (Xi*cos(10deg) - Yi*sin(10deg))
Yf = Xi*sin(10deg) + Yi*cos(10deg)
Note how the Xf is having an extra multiplier which is the culprit of creating the skew effect. It's like we changed the behavior or Xf and kept the Yf
Now let's consider rotate(10deg) scale(2, 1):
|cos(10deg) -sin(10deg)| |2 0| |2*cos(10deg) -1*sin(10deg) |
|sin(10deg) cos(10deg) | x |0 1| = |2*sin(10deg) 1*cos(10deg) |
And then we will have
Xf = 2*Xi*cos(10deg) - Yi*sin(10deg)
Yf = 2*Xi*sin(10deg) + Yi*cos(10deg)
We can consider the 2*Xi as an Xt and we can say that we rotated the (Xt,Yi) element and this element was initially scaled considering the X-axis.
1CSS uses also affine transformation (like translate) so using ℝ² (Cartesian coordinates) isn't enough to perform our calculation so we need to consider ℝℙ² (Homogeneous coordinates). Our previous calculation will be:
|2 0 0| |cos(10deg) -sin(10deg) 0| |2*cos(10deg) -2*sin(10deg) 0|
|0 1 0| x |sin(10deg) cos(10deg) 0| = |1*sin(10deg) 1*cos(10deg) 0|
|0 0 1| |0 0 1| |0 0 1|
Nothing will change in this case because the affine part is null but if we have a translation combined with another transform (ex: scale(2, 1) translate(10px,20px)) we will have the following:
|2 0 0| |1 0 10px| |2 0 20px|
|0 1 0| x |0 1 20px| = |0 1 20px|
|0 0 1| |0 0 1 | |0 0 1 |
And
Xf = 2*Xi + 20px;
Yf = Yi + 20px;
1 = 1 (to complete the multiplication)

The way Temani Afif explained it follows the coordinate systems that every transformation spans. You start with the viewport, and each consecutive coordinate system is derived and sits somewhere different on the canvas. These coordinate systems might turn out not be cartesian (a "stretched universe"). They are constructed in the DOM tree from the outside in, and when chained in an attribute, from left to right.
But you can imagine the same transformation also in the opposite direction, from the inside out: first you draw a rectangle in its cartesian userspace coordinate system, and than you transform it by a chain of scales, rotations and so on, until when drawing it in the viewport coordinate system, it is distorted to something else.
But if you look at it this the second way, the chained transformations in an attribute need to be processed right to left: transform: scale(2, 1) rotate(10deg) means take a rectangle, first rotate it by 10deg, and then scale the rotated rectangle in the horizontal direction.
In short, these two are equivalent:
If you draw a grafic in a transformed coordinate system, construct the coordinate system by applying transforms to these coordinate systems left-to-right.
If you draw a transformed grafic in the original coordinate system, construct the grafic by applying transforms to the grafic right-to-left.

Related

Why does order of transforms matter? rotate/scale doesn't give the same result as scale/rotate

After combing through the SVG specification, and guides such as this and this, I am still struggling to understand exactly how chaining transforms work.
Selected Relevant Quotes
When you apply the transform attribute to an SVG element, that element
gets a "copy" of the current user coordinate system in use.
And:
When transformations are chained, the most important thing to be aware
of is that, just like with HTML element transformations, each
transformation is applied to the coordinate system after that system
is transformed by the previous transformations.
And:
For example, if you’re going to apply a rotation to an element,
followed by a translation, the translation happens according to the
new coordinate system, not the inital non-rotated one.
And:
The sequence of transformations matter. The sequence the
transformation functions are specified inside the transform attribute
is the sequence they are applied to the shape.
Code
The first rectangle's current coordinate system is scaled, then rotated (note the order). The second rectangle's current coordinate system is rotated, then scaled.
svg {
border: 1px solid green;
}
<svg xmlns="http://www.w3.org/2000/svg">
<style>
rect#s1 {
fill: red;
transform: scale(2, 1) rotate(10deg);
}
</style>
<rect id="s1" x="" y="" width="100" height="100" />
</svg>
<svg xmlns="http://www.w3.org/2000/svg">
<style>
rect#s2 {
fill: blue;
transform: rotate(10deg) scale(2, 1);
}
</style>
<rect id="s2" x="" y="" width="100" height="100" />
</svg>
Question
We know that when we chain transforms, a copy is made of the current coordinate system in use for that element, then the transforms are applied in the order they are specified.
When we have a user coordinate system that is already scaled, and we apply a rotate to it, the rectangle is (as seen) effectively skewed (notice the changed angles). This does not happen if we do the two transforms the other way around (rotate, then scale).
Expert help on exactly how the scaled current coordinate system is rotated, would be deeply appreciated. I am trying to understand, from a technical (inner workings) angle, exactly why the skewing happens in the first rectangle.
Thank you.
To illustrate how it works let's consider an animation to show how the scaling effect change the rotation.
.red {
width:80px;
height:20px;
background:red;
margin:80px;
transform-origin:left center;
animation: rotate 2s linear infinite;
}
#keyframes rotate {
from{transform:rotate(0)}
to{transform:rotate(360deg)}
}
<div class="container">
<div class="red">
</div>
</div>
As you can see above, the rotation is creating a perfect circle shape.
Now let's scale the container and see the difference:
.red {
width:80px;
height:20px;
background:red;
margin:80px;
transform-origin:left center;
animation: rotate 5s linear infinite;
}
#keyframes rotate {
from{transform:rotate(0)}
to{transform:rotate(360deg)}
}
.container {
display:inline-block;
transform:scale(3,1);
transform-origin:left center;
}
<div class="container">
<div class="red">
</div>
</div>
Notice how we no more have a circle but it's an ellipse now. It's like we took the circle and we stertch it which is creating the skew effect inside our rectangle.
If we do the opposite effect and we start by having a scale effect and then we apply a rotation we won't have any skewing.
.red {
width:80px;
height:20px;
background:red;
margin:80px;
animation: rotate 2s linear infinite;
}
#keyframes rotate {
from{transform:scale(1,1)}
to{transform:scale(3,1)}
}
.container {
display:inline-block;
transform:rotate(30deg);
transform-origin:left center;
}
<div class="container">
<div class="red">
</div>
</div>
To explain it differently: Applying a rotation will keep the same ratio between both X and Y axis so you won't see any bad effect when doing scale later but scaling only one axis will break the ratio thus our shape we look bad when we try to apply a rotation.
You can check this link if you want more details about how transform are chained and how the matrix is caclulated: https://www.w3.org/TR/css-transforms-1/#transform-rendering. It's about HTML element but as said in the SVG specification it's the same.
Here is the relevant parts:
Transformations are cumulative. That is, elements establish their local coordinate system within the coordinate system of their parent.
From the perspective of the user, an element effectively accumulates all the transform properties of its ancestors as well as any local transform applied to it
Let's do some math in order to see the difference between both transformations. Let's consider matrix multiplication and since we are dealing with a 2D linear transformation we will do this on ℝ² for simplicity1.
For scale(2, 1) rotate(10deg) we will have
|2 0| |cos(10deg) -sin(10deg)| |2*cos(10deg) -2*sin(10deg) |
|0 1| x |sin(10deg) cos(10deg) | = |1*sin(10deg) 1*cos(10deg) |
Now if we apply this matrix to an (Xi,Yi) we will obtain (Xf,Yf) like below:
Xf = 2* (Xi*cos(10deg) - Yi*sin(10deg))
Yf = Xi*sin(10deg) + Yi*cos(10deg)
Note how the Xf is having an extra multiplier which is the culprit of creating the skew effect. It's like we changed the behavior or Xf and kept the Yf
Now let's consider rotate(10deg) scale(2, 1):
|cos(10deg) -sin(10deg)| |2 0| |2*cos(10deg) -1*sin(10deg) |
|sin(10deg) cos(10deg) | x |0 1| = |2*sin(10deg) 1*cos(10deg) |
And then we will have
Xf = 2*Xi*cos(10deg) - Yi*sin(10deg)
Yf = 2*Xi*sin(10deg) + Yi*cos(10deg)
We can consider the 2*Xi as an Xt and we can say that we rotated the (Xt,Yi) element and this element was initially scaled considering the X-axis.
1CSS uses also affine transformation (like translate) so using ℝ² (Cartesian coordinates) isn't enough to perform our calculation so we need to consider ℝℙ² (Homogeneous coordinates). Our previous calculation will be:
|2 0 0| |cos(10deg) -sin(10deg) 0| |2*cos(10deg) -2*sin(10deg) 0|
|0 1 0| x |sin(10deg) cos(10deg) 0| = |1*sin(10deg) 1*cos(10deg) 0|
|0 0 1| |0 0 1| |0 0 1|
Nothing will change in this case because the affine part is null but if we have a translation combined with another transform (ex: scale(2, 1) translate(10px,20px)) we will have the following:
|2 0 0| |1 0 10px| |2 0 20px|
|0 1 0| x |0 1 20px| = |0 1 20px|
|0 0 1| |0 0 1 | |0 0 1 |
And
Xf = 2*Xi + 20px;
Yf = Yi + 20px;
1 = 1 (to complete the multiplication)
The way Temani Afif explained it follows the coordinate systems that every transformation spans. You start with the viewport, and each consecutive coordinate system is derived and sits somewhere different on the canvas. These coordinate systems might turn out not be cartesian (a "stretched universe"). They are constructed in the DOM tree from the outside in, and when chained in an attribute, from left to right.
But you can imagine the same transformation also in the opposite direction, from the inside out: first you draw a rectangle in its cartesian userspace coordinate system, and than you transform it by a chain of scales, rotations and so on, until when drawing it in the viewport coordinate system, it is distorted to something else.
But if you look at it this the second way, the chained transformations in an attribute need to be processed right to left: transform: scale(2, 1) rotate(10deg) means take a rectangle, first rotate it by 10deg, and then scale the rotated rectangle in the horizontal direction.
In short, these two are equivalent:
If you draw a grafic in a transformed coordinate system, construct the coordinate system by applying transforms to these coordinate systems left-to-right.
If you draw a transformed grafic in the original coordinate system, construct the grafic by applying transforms to the grafic right-to-left.

Calculating CSS transform matrix for SVG animation of rotation during translation

The square should be animated to rotate while moving along a straight line from the red square to the blue square. I can't get this to work as you can see from the moving square never matching the fixed blue square.
That problem appears repeatedly, therefore I need a reusable solution. That is: don't just give specific numbers for the matrix but also the calculation by which you arrived at those values.
So I need a matrix() transform in terms of the following variables:
x1,y1 is the initial point of the straight movement path (in the example below 75,75);
x2,y2 is the final point of the straight movement path (in the example below 175,75);
cx,cy is the center of rotation (in the example below 75,75);
a as angle by which to rotate (in the example below 45deg - I would prefer giving the value in radians but CSS doesn't seem to accept that);
such that the object rotates around cx,cy by angle a while the point x1,y1 moves to x2,y2 along a straight line (note that while x1,y1 and cx,cy coincide in the example below this is not always the case, but the example was supposed to be simple).
Note that I do know how the matrices work, including how to compose transformations. The problem is that I do not know which particular transformations I need for this specific problem.
<?xml version="1.0" encoding="UTF-8" standalone="yes" ?>
<svg xmlns="http://www.w3.org/2000/svg"
xmlns:xlink="http://www.w3.org/1999/xlink"
width="250px" height="150px" viewBox="0 0 250 150">
<style>
*{stroke:black;stroke-width:1px;}
#keyframes ani {
0% {fill:#f00;transform:translate(0, 0 );}
/* illustrating that movement works without rotation. */
25% {fill:#00f;transform: translate( 100px, 0 );}
50% {fill:#f00;transform:translate(0, 0 );}
/* with rotation it ends up anywhere but where it is supposed to go. */
75% {fill:#00f;transform: translate( 100px, 0 ) translate( 175px, 75px) rotate( 45deg ) translate( -175px, -75px );}
100% {fill:#f00;transform:translate(0, 0 );}
}
#p {animation: ani 4000ms ease-in-out 10ms infinite normal forwards;}
</style>
<defs>
<path id="def" d="m0,0 l50,0 l0,50 l-50,0 l0,-50" />
</defs>
<use xlink:href="#def" x="50" y="50" style="fill:#faa;" />
<use xlink:href="#def" x="150" y="50" style="fill:#aaf;" transform="rotate( 45 175 75 )" />
<use id="p" xlink:href="#def" x="50" y="50" />
</svg>
The matrix I believe you want is basically this (thank you to this page for helping me figure it out):
transform: matrix(cos(angle),sin(angle),-sin(angle),cos(angle),translateDiffX,translateDiffY);
I don't think you can set trigonometry values in CSS (except perhaps with a pre-processor), so I made this fiddle where the CSS is overwritten via JavaScript (though in a sort of heavy-handed way), and changed up the angles and translation values to test to make sure it worked for other cases.
Note that without the transform-origin at the 0% and 100%, the animation has a sort of arc effect (though perhaps that's desirable/unimportant).

Breaking up transformations

I have 48 elements and they all have the same animation which is a transformation in the Y axis but they each have a different rotation
transform:rotate(0deg) translateY(-105px);
...
#keyframes mov {
0%,65%,100% {transform:rotate(0deg) translateY(-105px);}
15%, 50% {transform:rotate(0deg) translateY(105px);}
}
and it would be horribly inefficient if I were to have that for each element - is there a way I can have transform:rotate(0deg) in one place and translateY(?px); in another place?
Current solution: element in an element, first is rotate and the second is translateY

Unskewed child not aligning

Note: This question is about a problem with output, and not about creation of any shape.
I recently created a shape :
.prog {
position: relative;
top: 20px;
width: 150px;
height: 120px;
background: green;
display: inline-block;
transform: skewY(20deg);
transform-origin: 0% 100%;
transition: 0.8s ease;
}
.prog:before {
content: "";
position: absolute;
left: 150px;
width: 150px;
height: 120px;
background: red;
transform: skewY(-40deg);
transform-origin: 0% 100%;
transition: 0.8s ease;
}
<div class="prog "></div>
In the above snippet, the green shape is the .prog element, and is skewed. The red shape is :before pseudoelement of the first element.
I skewed .prog (skewY) to 20deg. Now, I needed :before to be -20deg. For this, I first had to unskew it. Then skew it further 20deg.
So final skewY value will be -40deg. I applied this, and appropriate transform-origins.
But the problem is that the top points of both the shapes aren't aligning. They should, but they aren't. Here's an image showing the problem :
The black lines are just for reference.
Now even more!
I skewed -20 -20 instead of -40 :
transform: skewY(-20deg) skewY(-20deg); <-- This works!
transform: skewY(-40deg); <---------------- This doesn't!
The behaviour of the "unskewed" child is normal, it is the way skew works. In order to understand this, I am going to simplify the question to :
why isn't skewX(40deg) the same as skewX(20deg) skewX(20deg)?
div {
width: 100px; height: 100px;
position:absolute;
top:20px; left:20px;
transform-origin: 0 0;
}
.d1 {
transform: skewX(40deg);
background: red;
opacity:0.7;
}
.d2 {
transform: skewX(20deg) skewX(20deg);
background: blue;
opacity:0.7;
}
/** FOR THE DEMO **/
body {background: url('http://i.stack.imgur.com/GySvQ.png');background-size: 10px;}
.m {text-align:right;padding-top:105px;}
.m1{width:83px;color:red;border-right:1px solid red;}
.m2 {width:72px;color:blue;border-right:1px solid blue;}
p{margin:0 0 5px 150px;color:red;}
.b{color:blue;}
<div class="d1"></div>
<div class="d2"></div>
<div class="m m1">x = 83</div>
<div class="m m2"><br/>x = 72</div>
<p class="r">skewX(40deg)</p>
<p class="b">skewX(20deg) skewX(20deg)</p>
Note: for the sake of explanation I will be using a 100*100 square div and the transform origin is set on the top left corner of this div. Like in the above code snippet.
To understand the difference between the two transformations, we need to explore the way the CSS skew() function works. The specs say :
A 2D skew transformation along the X axis with the parameter alpha is
equivalent to the matrix:
So this means we can calculate the coordinates of each point of a 2D X skewed element like this :
| 1 tan(α) | . | x |
| 0 1 | | y |
α is the X skewed angle
x/y the coordinates of the point before transformation
For skewX(40deg)
α = tan(40deg) ~= 0.83
| 1 0.83 | . | 0 | | 83 |
| 0 1 | | 100 | = | 100 |
The new coordinates are x = 83 as seen in the code snippet example.
For skewX(20deg) skewX(20deg)
α = tan(20deg) ~= 0.36
first skew :
| 1 0.36 | . | 0 | | 36 |
| 0 1 | | 100 | = | 100 |
Second skew :
| 1 0.36 | . | 36 | | 72 |
| 0 1 | | 100 | = | 100 |
The new coordinates are x = 72 as seen in the code snippet.
Conclusion
Both transformations don't give the same result. So skewY(20deg) skewY(-40deg) isn't the same transformation as skewY(-20deg) and the two top corners of the red and green elements can't align as :
tan(20deg) != tan(40deg)/2
References :
The CSS3 matrix() Transform for the Mathematically Challenged
CSS transform skew: math unveiled
CSS Transforms Module Level 1
skew introduces a vertical offset equal to the tangent of the angle. So, skew(20deg) introduces an offset of tan(20deg).
For your example to work, it should be that
tan(-20deg) = tan(20deg) + tan( -2 * 20deg)
or
tan (2 * x) = 2 * tan (x)
but this is not true, tangent and sum are not asociative
the required skew to reverse it is
result = - atan ( 2 * tan (x))
that, for x = 20, gives a result of
36,052388732387908475278040193987
(aproximately)
By way of a revision of my initial answer which produced an output rather than an explanation, I would postulate* that the effect is seen because you are surmising a negative skew can be used to offset a position on the positive skew curve, when in fact- at minus values you are operating on the negative skew curve.
This would first require that the measure of skew was singular and occurring on the same curve (see normal curve below), with positive and negative values allowing to shift along the curve.
However, the curve for negative and positive skews are inversly tailed.
Zero skew is the only value which operates the same on both. As such, if you have an element, apply a skew of 20 degrees to it, then apply a skew of minus 20 you will actually have a skew (positive or negative) of zero, so using a negative offset appears to work..
However, if you then apply additional negative skew, you will have a negatively skewed element, the curve for which is different and not equal to the inverse equivalent position on the positive skew curve.
20deg = Original element, 20deg on positive skew curve
20deg - 20deg = 0, same for positive and negative skew curve
-40deg = taking the elements current 20deg skew, minus 40 deg = 20deg on negative skew curve - NOT an equivalent 'opposite' point on the positive skew curve
When using psuedos, the skew works because you arent offsetting a positively skewed value by a newgatively skewed amount.
* Im no mathematician, so afraid I can only claim this as conjecture
I will just address to the main issue, as to why it happens, instead of trying to provide alternatives
Since your math appears to be exact, we have to search for what is not exact, which is the platform itself...
MDN's article on transform states that it is not a stable technology:
This is an experimental technology
Because this technology's specification has not stabilized, check the
(emphasis mine)
Consider the following:
When you skew the main div, it requires a huge amount of browser render calculations to show in a 2D environment what it would look like considering both 3 axis... The pseudo element suffer with those calculations, as you can see if you further it a little left from the edge where you've put it:
See that I just moved to the left, and that caused it to go down a little, according to the Y skewing applied to the main div. Now, add to that another huge amount of calculations when you re-skew the pseudo element... The browser will just fail to provide an accurate render of what the 3D space would look like in a 2D environment...

Rotate more than 360 deg using css 3 matrix

I am using CSS transitions like this:
div
{
-webkit-transform: rotate(0deg);
-webkit-transition: -webkit-transform 2s;
}
div:hover
{
-webkit-transform: rotate(720deg);
}
This effectively makes the div rotate 2 times for 2 seconds. Now I want to use the matrix so that I can rotate and scale the image; however the matrix does not use degrees but cos(a) and sin(a) and as we all know cos(0) = cos(360) = cos(720) etc. So using the matrix I am unable to rotate the image more than 359deg.
So I decided to be clever and with JavaScript I took the matrix from a rotated element (720deg) and it looks like this:
-webkit-transform: matrix(1, -0.0000000000000004898587196589413, 0.0000000000000004898587196589413, 1, 0, 0);
However using this matrix I am not able to rotate the element - I will later calculate the size and apply that too.
So the question is - how do I rotate an element more than 359deg using css 3 matrix transform?
Matrices in CSS 3 define
a mathematical mapping from one coordinate system into another
(W3C reference),
which implies that 720deg is exactly equivalent to 360deg as you pointed out. So you will not be able to do this directly with a matrix.
However, this syntax should work for your need :
div:hover {
transform: scale(1.5,1.5) rotate(720deg);
transition: transform 3s;
}

Resources