在 SVG 中旋转形状时保持旋转的线性渐变不旋转
Keeping a Rotated Linear Gradient Unrotated when Shape is Rotated in SVG
(SVG 或 WPF XAML - 我对两者都持开放态度(并且需要)。我不认为它们在实现上有任何不同。下面的示例是 SVG 格式的)。
我正在尝试找到一种在线性渐变(在本例中为 270°)上使用旋转的方法,但是当它填充的形状旋转时,保持线性渐变不变,就好像它是填充物不旋转。
例如,Microsoft Word 可以做到这一点。使用线性渐变填充自选图形时,您可以取消选中"Rotate with shape",然后无论您如何旋转形状,线性渐变都会保持原样。
所以,这是一个示例,我想将线性渐变在其中心旋转 270°(在 linearGradient
定义中,gradientTransform="rotate(270 0.5 0.5)"
)。之后,我想将它填充的路径旋转45°(在path
,transform="rotate(45 0.5 0.5)"
)。
<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)"
中。这让我很亲近。真的很近。于是我尝试降低角度,voilà,大约 221° 似乎是正确的。比270°-45°低4°。
所以我再次尝试此策略,路径旋转 65° - 我知道 linearGradient
的旋转(最初为 270°)不会是 205°,但接近于此,啊哈! 201°(或它的某个浮点数)比 270°-65° 低 4°。
我怎么知道这两种情况都比减法低4°而不是270°-65°?我盯着 linearGradient
中的分隔线以确保它是水平的。
那么有没有一种方法可以不考虑 linearGradient
旋转角度或形状的旋转角度来计算上述内容?例如,如果线性渐变的旋转是 180°(将上面的示例垂直划分)或任何其他角度?如果我能弄清楚如何在此处插入 JS Fiddle,我会放更多示例 运行.
更新(在 WPF 上):
@Clemens 对如何通过转换 PathGeometry
而不是 Path
在 WPF 中实现这一点提供了一些敏锐的见解。太棒了
这是转换 Path
的示例,与上面的 SVG 相同。
<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
的方法 - 它完全符合我的需要(好吧,除了旋转中心,但我想我可以解决这个问题)。
<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:
由于在元素上应用渐变的方式,角度不相加。默认..
(...) 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 度梯度:
<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 度角的渐变,想象一下将渐变压缩回正方形,然后计算角度看起来像什么。
长话短说,这里是一些代码:
(SVG 或 WPF XAML - 我对两者都持开放态度(并且需要)。我不认为它们在实现上有任何不同。下面的示例是 SVG 格式的)。
我正在尝试找到一种在线性渐变(在本例中为 270°)上使用旋转的方法,但是当它填充的形状旋转时,保持线性渐变不变,就好像它是填充物不旋转。
例如,Microsoft Word 可以做到这一点。使用线性渐变填充自选图形时,您可以取消选中"Rotate with shape",然后无论您如何旋转形状,线性渐变都会保持原样。
所以,这是一个示例,我想将线性渐变在其中心旋转 270°(在 linearGradient
定义中,gradientTransform="rotate(270 0.5 0.5)"
)。之后,我想将它填充的路径旋转45°(在path
,transform="rotate(45 0.5 0.5)"
)。
<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)"
中。这让我很亲近。真的很近。于是我尝试降低角度,voilà,大约 221° 似乎是正确的。比270°-45°低4°。
所以我再次尝试此策略,路径旋转 65° - 我知道 linearGradient
的旋转(最初为 270°)不会是 205°,但接近于此,啊哈! 201°(或它的某个浮点数)比 270°-65° 低 4°。
我怎么知道这两种情况都比减法低4°而不是270°-65°?我盯着 linearGradient
中的分隔线以确保它是水平的。
那么有没有一种方法可以不考虑 linearGradient
旋转角度或形状的旋转角度来计算上述内容?例如,如果线性渐变的旋转是 180°(将上面的示例垂直划分)或任何其他角度?如果我能弄清楚如何在此处插入 JS Fiddle,我会放更多示例 运行.
更新(在 WPF 上):
@Clemens 对如何通过转换 PathGeometry
而不是 Path
在 WPF 中实现这一点提供了一些敏锐的见解。太棒了
这是转换 Path
的示例,与上面的 SVG 相同。
<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
的方法 - 它完全符合我的需要(好吧,除了旋转中心,但我想我可以解决这个问题)。
<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:
由于在元素上应用渐变的方式,角度不相加。默认..
(...) 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 度梯度:
<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 度角的渐变,想象一下将渐变压缩回正方形,然后计算角度看起来像什么。
长话短说,这里是一些代码: