user66554 user66554 - 6 months ago 18
CSS Question

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>




Answer

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).