I am trying to translate part of a SVG graphic after the entire graphic has already been rotated. It consists of two triangle-shaped brackets that are first scaled in, and then rotated. After that's done, I simply want the right bracket to shift right on the x-axis while the left bracket stays in place.
Scaling and rotating the elements around their center was not a problem, however when I want to translate the right bracket on the x-axis, I am getting unexpected side-effects.
Here is a working snippet that illustrates the problem:
.brackets {
animation: scaling 1s, rotating 2s 1s;
transform-box: fill-box;
}
.bracket-left {
animation: rotate-left 1s 3s forwards;
transform-box: fill-box;
}
.bracket-right {
animation: sliding 1s 3s forwards;
transform-box: fill-box;
}
@keyframes scaling {
0% {
transform: scale(0);
}
25% {
transform: scale(1);
}
100% {
transform: scale(1);
}
}
@keyframes rotating {
0% {
transform-origin: center;
transform: rotate(0deg);
}
100% {
transform-origin: center;
transform: rotate(-405deg);
}
}
@keyframes sliding {
100% {
transform: translate(40px, 0px) rotate(-45deg);
}
}
@keyframes rotate-left {
0% {
transform-origin: center;
transform: rotate(-45deg);
}
100% {
transform-origin: center;
transform: rotate(-45deg);
}
}
<html lang="en">
<head>
<meta charset="utf-8" />
<meta name="viewport" content="width=device-width, initial-scale=1" />
<title>CSS SVG</title>
<link rel="stylesheet" href="test.css" />
</head>
<body>
<svg
width="256"
height="256"
viewbox="0 0 100 100"
xmlns:xlink="http://www.w3.org/1999/xlink"
xmlns="http://www.w3.org/2000/svg"
>
<defs>
<linearGradient
xlink:href="#a"
id="e"
gradientUnits="userSpaceOnUse"
x1="-3.999"
y1=".503"
x2="-.497"
y2="4.005"
/>
<linearGradient id="a">
<stop
style="stop-color: #17ce17; stop-opacity: 0.80000001"
offset="0"
/>
<stop
style="stop-color: #11b3d4; stop-opacity: 0.49803922"
offset=".5"
/>
<stop style="stop-color: #00f; stop-opacity: 0" offset=".5" />
</linearGradient>
<linearGradient
xlink:href="#b"
id="f"
gradientUnits="userSpaceOnUse"
x1="1.906"
y1="1.889"
x2="15.117"
y2="15.107"
/>
<linearGradient id="b">
<stop style="stop-color: #17ceb5; stop-opacity: 1" offset=".364" />
<stop style="stop-color: #05fa05; stop-opacity: 0" offset="1" />
</linearGradient>
<linearGradient
xlink:href="#a"
id="c"
gradientUnits="userSpaceOnUse"
x1="-3.999"
y1=".503"
x2="-.497"
y2="4.005"
/>
<linearGradient
xlink:href="#b"
id="d"
gradientUnits="userSpaceOnUse"
x1="1.906"
y1="1.889"
x2="15.117"
y2="15.107"
/>
</defs>
<g style="display: inline">
<g style="display: inline">
<path
style="
display: inline;
fill: url(#c);
fill-rule: evenodd;
stroke-width: 0.264583;
"
transform="rotate(90 0 41.401) scale(3.77953)"
d="M-3.5 0h3a.499.499 0 1 1 0 1h-3a.499.499 0 1 1 0-1Z"
/>
<path
style="
display: inline;
fill: url(#d);
fill-opacity: 1;
fill-rule: evenodd;
stroke-width: 0.999999;
"
d="M1.89 0C.845 0 .002.845 0 1.89v3.78a1.89 1.89 0 0 1 1.885-1.89h11.344a1.884 1.884 0 0 0 1.888-1.89C15.117.845 14.275 0 13.23 0Zm1.89 5.67a1.89 1.89 0 0 1-.009.17h.008z"
transform="rotate(179.997 20.7 20.7)"
/>
</g>
<g style="display: inline">
<path
style="
display: inline;
fill: url(#e);
fill-rule: evenodd;
stroke-width: 0.264583;
"
transform="rotate(-90 22.599 0) scale(3.77953)"
d="M-3.5 0h3a.499.499 0 1 1 0 1h-3a.499.499 0 1 1 0-1Z"
/>
<path
style="
display: inline;
fill: url(#f);
fill-opacity: 1;
fill-rule: evenodd;
stroke-width: 0.999999;
"
d="M1.89 0C.845 0 .002.845 0 1.89v3.78a1.89 1.89 0 0 1 1.885-1.89h11.344a1.884 1.884 0 0 0 1.888-1.89C15.117.845 14.275 0 13.23 0Zm1.89 5.67a1.89 1.89 0 0 1-.009.17h.008z"
transform="matrix(1 0 0 1 22.599 22.598)"
/>
</g>
</g>
</svg>
</body>
</html>
This is the closest I have gotten to so far. Note how the left bracket looks like it is translating during the very last animation, even though I only have a rotate active on it. I also don't want the right bracket to move on the y-axis, just the x-axis.
I am not quite sure why exactly this happens, but I think it's related to the rotation also modifying the SVG's coordinate system. I already tried nesting each of the brackets as a SVG inside the main SVG, but either I was too dumb to do that correctly, or it didn't help.
How can I achieve this? What's the best way to handle transforms modifying the SVG's coordinate system when animating different/combined SVG-graphics?
CodePudding user response:
Problems like this are almost always caused by one of two things:
- Accidentally replacing an existing transform with a non-equivalent one, or
- The transform origin changing unexpectedly
In your case, I believe it is the latter. When you move the .bracket-right
, the point corresponding to transform-origin: center
moves. That is because the fill-box
is getting bigger. And that affects what the combined set of transforms produces.
I would recommend simplifying your animations. You really only have two transforms happening:
- The scale and rotate of both brackets
- The movement of the right bracket
The most important change I have made below is to (a) remove transform-box: fill
and (b) use absolute coordinates for the transform-origin
.
For the initial scale, I use transform-origin: 22.6px, 22.6px
. Which corresponds to the top-left of the brackets. And for the rotation I use transform-origin: 32px, 32px
, which corresponds to the centre point of the two brackets. And because I am using absolute coordinates, the transforms aren't affected when the right bracket moves.
As for the right bracket animation, I simplified it to a simple translate down and to the right. Because that is what it really is if you think about the original un-rotated icon.
.brackets {
animation: anim-both 3s forwards;
}
.bracket-right {
animation: anim-right 1s 3s forwards;
}
@keyframes anim-both {
0% {
transform: rotate(0deg) scale(0);
transform-origin: 22.6px 22.6px;
}
33% {
transform: rotate(0deg) scale(1);
transform-origin: 22.6px 22.6px;
}
34% {
transform: rotate(0deg) scale(1);
transform-origin: 32px 32px;
}
100% {
transform: rotate(-405deg) scale(1);
transform-origin: 32px 32px;
}
}
@keyframes anim-right {
0% {
transform: translate(0, 0);
}
100% {
transform: translate(40px, 40px);
}
}
<html lang="en">
<head>
<meta charset="utf-8" />
<meta name="viewport" content="width=device-width, initial-scale=1" />
<title>CSS SVG</title>
<link rel="stylesheet" href="test.css" />
</head>
<body>
<svg
width="256"
height="256"
viewbox="0 0 100 100"
>
<defs>
<linearGradient
xlink:href="#a"
id="e"
gradientUnits="userSpaceOnUse"
x1="-3.999"
y1=".503"
x2="-.497"
y2="4.005"
/>
<linearGradient id="a">
<stop
style="stop-color: #17ce17; stop-opacity: 0.80000001"
offset="0"
/>
<stop
style="stop-color: #11b3d4; stop-opacity: 0.49803922"
offset=".5"
/>
<stop style="stop-color: #00f; stop-opacity: 0" offset=".5" />
</linearGradient>
<linearGradient
xlink:href="#b"
id="f"
gradientUnits="userSpaceOnUse"
x1="1.906"
y1="1.889"
x2="15.117"
y2="15.107"
/>
<linearGradient id="b">
<stop style="stop-color: #17ceb5; stop-opacity: 1" offset=".364" />
<stop style="stop-color: #05fa05; stop-opacity: 0" offset="1" />
</linearGradient>
<linearGradient
xlink:href="#a"
id="c"
gradientUnits="userSpaceOnUse"
x1="-3.999"
y1=".503"
x2="-.497"
y2="4.005"
/>
<linearGradient
xlink:href="#b"
id="d"
gradientUnits="userSpaceOnUse"
x1="1.906"
y1="1.889"
x2="15.117"
y2="15.107"
/>
</defs>
<g >
<g >
<path
style="
fill: url(#c);
fill-rule: evenodd;
stroke-width: 0.264583;
"
transform="rotate(90 0 41.401) scale(3.77953)"
d="M-3.5 0h3a.499.499 0 1 1 0 1h-3a.499.499 0 1 1 0-1Z"
/>
<path
style="
fill: url(#d);
fill-opacity: 1;
fill-rule: evenodd;
stroke-width: 0.999999;
"
d="M1.89 0C.845 0 .002.845 0 1.89v3.78a1.89 1.89 0 0 1 1.885-1.89h11.344a1.884 1.884 0 0 0 1.888-1.89C15.117.845 14.275 0 13.23 0Zm1.89 5.67a1.89 1.89 0 0 1-.009.17h.008z"
transform="rotate(179.997 20.7 20.7)"
/>
</g>
<g >
<path
style="
fill: url(#e);
fill-rule: evenodd;
stroke-width: 0.264583;
"
transform="rotate(-90 22.599 0) scale(3.77953)"
d="M-3.5 0h3a.499.499 0 1 1 0 1h-3a.499.499 0 1 1 0-1Z"
/>
<path
style="
fill: url(#f);
fill-opacity: 1;
fill-rule: evenodd;
stroke-width: 0.999999;
"
d="M1.89 0C.845 0 .002.845 0 1.89v3.78a1.89 1.89 0 0 1 1.885-1.89h11.344a1.884 1.884 0 0 0 1.888-1.89C15.117.845 14.275 0 13.23 0Zm1.89 5.67a1.89 1.89 0 0 1-.009.17h.008z"
transform="matrix(1 0 0 1 22.599 22.598)"
/>
</g>
</g>
</svg>
</body>
</html>
CodePudding user response:
I would take a very different approch. Instead of filled paths I would use strokes with stroke-linecap="round"
In the next example the strokes are black but you can use your gradiente if desired.
Please observe that I've changed the value of the first 2 parameters of the viewBox attribute because I wanted to center the brackets around 0,0 This is simplifying the code a lot.
Observation: I've keeped the same size and aspect ratio of the svg as in your code but I would change this to a smaller canvas. To understand what I mean I've added a silver background to the svg canvas.
svg{background:silver}
path{animation: a 1s forwards;}
g{animation: b 1s 1s forwards;}
@keyframes a{
100% {
transform: rotate(-45deg);
}
}
@keyframes b{
100% {
transform: translate(20px,0px);
}
}
<svg width="256" height="256" viewbox="-20 -20 100 100" fill="none" stroke="black" stroke-width="4" stroke-linecap="round">
<path d="M-7.5,-7.5h11" />
<path d="M-7.5,-7.5v11" />
<g>
<path d="M7.5,7.5h-11" />
<path d="M7.5,7.5v-11" />
</g>
</svg>