【问题标题】:SVG coordinate system rotation skews anglesSVG坐标系旋转倾斜角度
【发布时间】:2019-05-09 01:59:24
【问题描述】:

我认为我对 SVG 有相当的了解,包括 viewportviewBox用户坐标系

在下面的第一个示例中,我们使用与 viewport 具有相同纵横比的 viewBox。正如预期的那样,用户坐标系旋转不会扭曲任何角度。

在示例二中,我们将 viewbox 设置为与 viewport 不同的纵横比。换句话说,当将 viewBox 映射到 viewport 时,不会保持形状的纵横比。右下角不会因这种缩放而失真,这是有道理的,因为坐标系原点位于 (0,0)。

然而,当我们在示例二中旋转用户坐标系时,右下角会发生扭曲。这在示例一中不会发生。

编辑 1: 需要明确的是,问题在于上一个示例中的右下角。旋转前,用viewBox拉伸后,角度为90%。然而,旋转后,它不再是 90%。

为什么不均匀缩放的三角形在旋转时会失去角度?

示例一(统一比例)

body {
  height: 500px;
}

svg {
  width: 400px;
  height: 400px;
  border: 1px solid red;
}
<svg id="s1" xmlns="http://www.w3.org/2000/svg" viewbox="0 0 200 200" preserveAspectRatio="none">
    <style>
      polygon {
        transform: translate(100px, 0px);
        animation: 2s ease-in 1s 1 normal forwards rotate-down;
        fill: green;
      }
      
      @keyframes rotate-down {
        0% {
          transform: translate(100px, 0px) rotate(0deg);
        }
        100% {
          transform: translate(100px, 0px) rotate(45deg);
        }
      }
    </style>
    <polygon points="100,100 100,0 0,100" />
  </svg>

示例二(非统一比例)

body {
  height: 500px;
}

svg {
  width: 600px;
  height: 400px;
  border: 1px solid red;
}
<svg id="s1" xmlns="http://www.w3.org/2000/svg" viewbox="0 0 200 400" preserveAspectRatio="none">
    <style>
      polygon {
        transform: translate(100px, 0px);
        animation: 2s ease-in 1s 1 normal forwards rotate-down;
        fill: green;
      }
      
      @keyframes rotate-down {
        0% {
          transform: translate(100px, 0px) rotate(0deg);
        }
        100% {
          transform: translate(100px, 0px) rotate(45deg);
        }
      }
    </style>
    <polygon points="100,100 100,0 0,100" />
  </svg>

编辑 2(图片澄清):

下面我们看到三角形 after viewBox 已添加(因此缩放和平移),但 before 旋转。右下角是90度。

下面我们看到三角形 viewBox 被添加(因此被缩放和平移), 旋转之后。右下角不再是 90 度。


编辑 3:

我终于明白了。

以下是解释详细信息并链接到相关资源的答案。

【问题讨论】:

  • 你在哪里看到扭曲的角度?三角形只是被拉伸了。您想如何拉伸三角形并同时保持 90 度角?拉伸正在改变角度(至少对于矩形而言)。
  • @Magnus 但它做到这一点的拉伸。
  • @enxaneta 我完全了解preserveAspectRatio 的作用,以及viewBox 结合该属性如何在当前用户坐标系(UCS)内缩放图形。这不是我的困惑所在。我认为首先应用了 viewBox,然后将其他转换应用于 viewBox 创建的修改后的 UCS 的副本。我已经通过规范的那部分几次,这就是我的理解的来源。
  • “transform 属性建立一个新的用户空间”。它在元素内建立了一个新的用户坐标系。但它不会覆盖当前的。它仍然受到在它之外应用的其他变换的影响。例如,无论 SVG 内部发生什么其他变换,SVG 中的所有内容都会受到 viewBox 变换的影响。
  • 不要进行编辑以添加解决方案/答案 .. 而是回答您自己的问题

标签: html css svg


【解决方案1】:

希望这个例子能告诉你发生了什么。

将鼠标悬停在 SVG 上,看看为什么是拉伸改变了角度。

body {
  height: 500px;
}

svg {
  width: 200px;
  height: 400px;
  border: 1px solid red;
  transition: 1s width;
}

svg:hover {
  width: 600px;
}
<svg id="s1" xmlns="http://www.w3.org/2000/svg" viewbox="0 0 200 400" preserveAspectRatio="none">
    <style>
      polygon {
        transform: translate(100px, 0px) rotate(45deg);
        fill: green;
      }
    </style>
    <polygon points="100,100 100,0 0,100" />
  </svg>

【讨论】:

  • 谢谢保罗。为了缩小我的问题,我创建了以下笔,其中 viewBox 被 transform: scale(2,1) (具有相同的效果)替换:codepen.io/magnusriga/pen/EOqqxb。然后我们继续添加translate(50px, 20px),最后添加rotate(45deg)。我们可以一步一步来吗?首先,创建并缩放初始用户坐标系 (UCS) 的副本,宽度加倍,高度不变。 2) 我们将整个坐标系向右移动 50 像素,向下移动 20 像素。由于之前应用的比例,这实际上是视口中的 100px 和 20px。 (1/2)
  • ... 3) 然后我们添加 45 度的旋转。不知何故,这对 x 轴和 y 轴的应用不同。由于与上一次平移发生的原理相同,沿 x 轴的坐标可能旋转了 y 轴的两倍。
  • 你把它背到前面了。您可以将变换视为从最后一个应用到第一个。所以首先将多边形旋转 45 度,然后向右移动 50 度和向下移动 20 度,最后水平缩放 2 倍。
  • 谢谢保罗。你知道规范中的哪个地方规定了这种行为吗?这个流行的 SVG 指南似乎表明了相反的观点:sarasoueidan.com/blog/svg-transformations/…。实际上,本指南也陈述了完全相反的情况:tutorials.jenkov.com/svg/…(“在 transform 属性中指定的转换函数的顺序是它们应用于形状的顺序。”)
  • 这取决于你是从坐标空间的角度考虑,还是从对象的角度考虑。变换从左到右应用于坐标空间。但是,如果您是从对象的角度考虑它,那么从内到外更容易将您的头包裹在它周围。要计算出正方形的最终位置,首先应用它的变换,然后是它的父代,然后是它的所有祖先。
【解决方案2】:

您似乎认为 viewBox 是在计算 SVG 图像时应用的某种变换方法,就像其他方法一样,这是不正确的。您在这里体验的是在整个 SVG 元素上应用的转换。要应用此转换,浏览器需要计算 SVG 对象,因此所有 SVG 内转换都已应用。

这与缩放光栅图像完全一样:

polygon {
  fill: transparent;
  stroke-width: 4px;
  stroke: black;
}
Base raster image:<br>
<img src="https://i.stack.imgur.com/ZA16O.png">

<br>Stretched raster image:<br>
<img style="height: 150px; width: 300px" src="https://i.stack.imgur.com/ZA16O.png">

<br>Base SVG:<br>
<svg viewBox="0,0,160,160" style="height: 120px" preserveAspectRatio="none">
<polygon points="10,10 150,10 80,150"/>
</svg>

<br>Stretched SVG:<br>
<svg viewBox="0,0,160,160" preserveAspectRatio="none" style="height: 120px; width: 300px;">
<polygon points="10,10 150,10 80,150"/>
</svg>

只有 SVG 被转换后才会被绘制,因此它们不会损失质量。


SVG 规范实际上说 (here) 应用于 SVG 元素的所有转换都以这种方式工作。

【讨论】:

  • 我彻底明白了自己的困惑。如果有兴趣,答案现在在 OP 中。
  • viewBox 与问题无关,但既然你提出来,我必须注意 viewBox 和变换(单个、链接或嵌套)都会创建当前用户坐标系统的副本,然后修改它。在计算出最终的变换矩阵(通过仿射变换矩阵的点积)之前,图形元素不会映射到该坐标系中。
  • viewBox的效果和svg元素本身的transform是一样的。我将在单独的 cmets 中发布规范的相关部分。
  • 'viewBox' 属性的存在会导致将变换应用于视口坐标系,如“计算 SVG 视口的等效变换”中所述。 - @ 987654322@。最后提到的部分显示了 viewBox 所暗示的转换,在这里:w3.org/TR/SVG/coords.html#ComputingAViewportsTransform
  • 'viewBox' 属性的作用是用户代理自动提供适当的变换矩阵来将用户坐标系中的指定矩形映射到指定区域的边界(通常是SVG 视口)。 - w3.org/TR/SVG/coords.html#ViewBoxAttribute
【解决方案3】:

我终于明白了。

以下问题是我在总结出实际问题后发布的,它解释了为什么坐标转换的行为如此:

在对该问题的回答中,@TemaniAfif 展示了如何计算最终转换矩阵并将其应用于图形元素的坐标,以便将其从视口坐标系映射到最终用户坐标系。

长话短说,在应用变换时,我们实际上所做的是复制当前用户坐标系,然后相对于我们从中复制的当前用户坐标系进行平移。在 SVG 中,初始用户坐标系(在 viewBox 或任何变换之前)与初始视口坐标系相同。

链式/嵌套变换应用于坐标系left-to-right / outside-in,以达到图形元素可以在其中的最终坐标系被映射。请注意,嵌套变换与在一个元素上链接变换具有相同的效果。

这实际上是如何工作的?好吧,每个变换都有一个预定义的仿射变换矩阵,与 CSS/SVG 无关。有几篇维基百科文章显示了这些矩阵,例如:

为了将元素的坐标映射到最终的用户坐标系,我们将矩阵从左到右(在源代码中写入的顺序)相互叠加,以达到最终的变换矩阵。

请注意,由于我们按照它们在源代码中编写的顺序来乘以变换矩阵,并且由于在乘以矩阵时 AxB 与 BxA 不同,因此在源代码中编写变换的顺序很重要。

最后,我们将元素的 x 和 y 坐标与这个最终的变换矩阵相乘,看看每个坐标是如何从视口坐标系映射到最终用户坐标系的。

对于那些如此倾向的人,可能更容易考虑以上内容,而只是在脑海中想象链接/嵌套变换应用于元素本身(而不是用户坐标系)对-to-to-left / inside-out(即与应用于坐标系的顺序相反)。

无论您是在脑海中想象从左到右变换坐标系然后映射到图形元素中,还是通过应用从右到左的变换来变换元素本身,最终结果将是相同的。

相关规范

注意

对于这个问题,变换是应用于 SVG 元素还是应用于 HTML 元素并不重要。同样的转换机制也适用。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2016-09-04
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2016-02-03
    相关资源
    最近更新 更多