Home > Mobile >  How to correctly translate part of SVG graphic after its parenthas been rotated?
How to correctly translate part of SVG graphic after its parenthas been rotated?

Time:12-22

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:

  1. Accidentally replacing an existing transform with a non-equivalent one, or
  2. 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:

  1. The scale and rotate of both brackets
  2. 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>

  • Related