关于 wpf:在 SVG 中旋转形状时保持旋转的线性渐变不旋转

Keeping a Rotated Linear Gradient Unrotated when Shape is Rotated in SVG

(SVG 或 WPF XAML - 我对(并且需要)两者都持开放态度。我认为它们在实现上没有任何不同。下面的示例是在 SVG 中)。

我正在尝试找到一种在线性渐变(在本例中为 270°)上使用旋转的方法,但是当它填充的形状旋转时,保持线性渐变不变,就好像它填充的形状是没有旋转。

例如,这是 Microsoft Word 可以做到的。当使用线性渐变填充自选图形时,您可以取消选中"随形状旋转",然后无论您如何旋转形状,线性渐变都将保持在原位。

所以,这里有一个例子,我想将线性渐变在其中心旋转 270°(在 linearGradient 定义中,gradientTransform="rotate(270 0.5 0.5)")。之后,我想将它填充的路径旋转 45°(在 pathtransform="rotate(45 0.5 0.5)" 中)。

1
2
3
4
5
6
7
8
9
10
11
12
13
<svg width="960" height="540" xmlns="http://www.w3.org/2000/svg" xmlns:svg="http://www.w3.org/2000/svg">
 <svg x="265.24353" y="133.125046" width="337.287231" height="211.206543" viewBox="0 0 337.287231 211.206543" style="overflow: visible;">
    <defs>
    <linearGradient id="linear"  gradientTransform="rotate(270 0.5 0.5)" spreadMethod="pad">
      <stop offset="0%" stop-color="#FF0000"/>
      <stop offset="48%" stop-color="#FF0000"/>
      <stop offset="48%" stop-color="#0070C0"/>
      <stop offset="100%" stop-color="#0070C0"/>
    </linearGradient>
  </defs>
  <path id="9" fill="url(#linear)"  transform="rotate(45 0.5 0.5)" stroke="#000000" stroke-width="5" stroke-linecap="round" d="M0,0L205.8147,0L205.8147,174.9073L0,174.9073Z"/>
  </svg>
</svg>

好的,所以这行不通,对吧?这是不应该的。所以我尝试从 270°(lin grad 的旋转)中减去 45°(路径的旋转)为 225°,然后在 linearGradient 中设置 gradientTransform="rotate(225 0.5 0.5)"。这让我很接近。真的很近。然后我尝试降低angular,瞧,大约 221° 似乎是正确的。低于 270°-45° 4°。

所以我再次尝试此策略,路径旋转 65° - 我知道 linearGradient 的旋转(最初为 270°)不会是 205°,但在某个接近的地方,啊哈!它是 201°(或某个浮点数)比 270°-65° 低 4°。

我怎么知道在这两种情况下它都比减法低 4° 比 270°-65° 低?我观察 linearGradient 中的分隔线以确保它是水平的。

那么是否有一种方法可以计算上述内容,而不管 linearGradient 旋转angular或形状的旋转angular如何?例如,如果线性梯度的旋转是 180°(垂直分割上面的例子)或任何其他angular?如果我能弄清楚如何在此处插入 JS Fiddle,我会提供更多可以运行的示例。

更新(在 WPF 上):

@Clemens 对如何在 WPF 中通过转换 PathGeometry 而不是 path 来实现这一点提供了一些深刻的见解。太棒了。

这是转换 path 的示例,它与上面的 SVG 相同。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
<Path RenderTransformOrigin="0.5,0.5" Data="M0,0L205.8147,0L205.8147,174.9073L0,174.9073Z" Stroke="Black" StrokeLineJoin="Miter" StrokeThickness="5">
        <Path.RenderTransform>
            <TransformGroup>
                <RotateTransform Angle="45" CenterY="0.5" CenterX="0.5"/>
            </TransformGroup>
        </Path.RenderTransform>
        <Path.Fill>
            <LinearGradientBrush StartPoint="0,1" EndPoint="1,1">
                <LinearGradientBrush.RelativeTransform>
                    <TransformGroup>
                        <RotateTransform Angle="221" CenterY="0.5" CenterX="0.5"/>
                    </TransformGroup>
                </LinearGradientBrush.RelativeTransform>
                <GradientStopCollection>
                    <GradientStop Offset="0" Color="#FF0000" />
                    <GradientStop Offset="0.48" Color="#FF0000" />
                    <GradientStop Offset="0.48" Color="#0070C0" />
                    <GradientStop Offset="1" Color="#0070C0" />
                </GradientStopCollection>
            </LinearGradientBrush>
        </Path.Fill>
    </Path>

这是仅转换 PathGeometry 的方法 - 它完全按照我的需要工作(嗯,除了旋转中心,但我想我可以解决这个问题)。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
<Path RenderTransformOrigin="0.5,0.5" Stroke="Black" StrokeLineJoin="Miter" StrokeThickness="5" Canvas.Left="15" Canvas.Top="23">
        <Path.Data>
            <PathGeometry>
                <PathGeometry.Figures>
                    <PathFigure StartPoint="0,0" IsClosed="True">
                        <LineSegment Point="205.8147,0"/>
                        <LineSegment Point="205.8147,174.9073"/>
                        <LineSegment Point="0,174.9073"/>
                    </PathFigure>
                </PathGeometry.Figures>
                <PathGeometry.Transform>
                    <RotateTransform Angle="45" CenterY="0.5" CenterX="0.5"/>
                </PathGeometry.Transform>
            </PathGeometry>
        </Path.Data>
        <Path.Fill>
            <LinearGradientBrush StartPoint="0,1" EndPoint="1,1">
                <LinearGradientBrush.RelativeTransform>
                    <TransformGroup>
                        <RotateTransform Angle="270" CenterY="0.5" CenterX="0.5"/>
                    </TransformGroup>
                </LinearGradientBrush.RelativeTransform>
                <GradientStopCollection>
                    <GradientStop Offset="0" Color="#FF0000" />
                    <GradientStop Offset="0.48" Color="#FF0000" />
                    <GradientStop Offset="0.48" Color="#0070C0" />
                    <GradientStop Offset="1" Color="#0070C0" />
                </GradientStopCollection>
            </LinearGradientBrush>
        </Path.Fill>
    </Path>


仅 SVG:

由于渐变应用于元素的方式,angular不会相加。默认情况下..

(...) It essentially scales the gradient to the size of your object, so you only have to specify coordinates in values from zero to one, and they're scaled to the size of your object automatically for you.

https://developer.mozilla.org/en-US/docs/Web/SVG/Tutorial/Gradients

另一种看待它的方式;渐变首先应用于正方形,然后拉伸以适合您元素的边界框。请参阅此示例,其中所有三个元素都具有相同的 45 度渐变:

1
2
3
4
5
6
7
8
9
10
11
12
<svg width="960" height="540" xmlns="http://www.w3.org/2000/svg">
  <defs>
    <linearGradient id="linear" gradientTransform="rotate(45 0.5 0.5)" spreadMethod="pad">
      <stop offset="49%" stop-color="gold"/>
      <stop offset="51%" stop-color="blue"/>
    </linearGradient>
  </defs>
 
  <path fill="url(#linear)" d="M0,0   h170 v100 h-170"/>
  <path fill="url(#linear)" d="M210,0 h100 v100 h-100"/>
  <path fill="url(#linear)" d="M350,0 h100 v170 h-100"/>
</svg>

因此,在您的情况下,如果您希望渐变为 225 度角,请想象将渐变压缩回方形,然后计算该angular的样子。

Angle

长话短说,这里有一些代码:

https://jsfiddle.net/bc4k4Lcv/