如何在其父级旋转后正确翻译部分 SVG 图形?

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

我正在尝试在旋转整个图形后翻译部分 SVG 图形。它由两个先按比例缩小然后旋转的三角形支架组成。 完成后,我只希望右括号在 x 轴上向右移动,而左括号保持原位。

围绕元素中心缩放和旋转元素不是问题,但是当我想平移 x 轴上的右括号时,我遇到了意想不到的副作用。

这是一个说明问题的工作片段:

    .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 class="brackets" style="display: inline">
        <g class="bracket-right" 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 class="bracket-left" 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>

这是我迄今为止最接近的一次。请注意左括号在最后一个动画中看起来是如何平移的,即使我只在其上激活了旋转。我也不希望右括号在y轴上移动,只在x轴上移动。

我不太清楚为什么会发生这种情况,但我认为这与旋转有关,同时也修改了 SVG 的坐标系。我已经尝试将每个括号作为 SVG 嵌套在主 SVG 中,但要么我太笨不能正确地做到这一点,要么没有帮助。

我怎样才能做到这一点?在动画化 different/combined SVG 图形时处理修改 SVG 坐标系的变换的最佳方法是什么?

我会采取截然不同的方法。我会使用带有 stroke-linecap="round"

的笔触而不是填充路径

在下一个示例中,笔画是黑色的,但如果需要,您可以使用渐变色。

请注意,我已经更改了 viewBox 属性的前 2 个参数的值,因为我想使括号围绕 0,0 居中。这大大简化了代码。

观察:我保持了与您的代码中相同的 svg 大小和纵横比,但我会将其更改为更小的 canvas。为了理解我的意思,我在 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>

像这样的问题几乎总是由以下两种情况之一引起的:

  1. 不小心将现有转换替换为非等效转换,或者
  2. 变换原点意外改变

你的情况,我认为是后者。当您移动 .bracket-right 时,对应于 transform-origin: center 的点也会移动。那是因为 fill-box 越来越大了。这会影响转换组合产生的结果。

我会建议简化您的动画。您实际上只发生了两个转换:

  1. 两个支架的缩放和旋转
  2. 右括号的移动

我在下面所做的最重要的更改是 (a) 删除 transform-box: fill 和 (b) 使用绝对坐标作为 transform-origin.

对于初始比例,我使用 transform-origin: 22.6px, 22.6px。对应于括号的左上角。对于旋转,我使用 transform-origin: 32px, 32px,它对应于两个括号的中心点。而且因为我使用的是绝对坐标,所以当右括号移动时变换不会受到影响。

至于右括号动画,我将其简化为简单的向下和向右平移。因为如果您考虑原始未旋转的图标,这就是它的真实情况。

.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 class="brackets">
        <g class="bracket-right">
          <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 class="bracket-left">
          <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>