在椭圆内裁剪和缩放图像(不是椭圆本身)
Clipping and scaling an image within an ellipse (not the ellipse itself)
我正在尝试找到一种方法来剪辑带有椭圆的图像,这样 如果我缩放图像,我可以保持椭圆完好无损,但到目前为止我已经运气不好。
这是我的 XAML:
<Grid>
<Grid.Clip>
<RectangleGeometry x:Name="ClippingRectangle"
Rect="0,0,200,200"/>
</Grid.Clip>
<Ellipse Stretch="UniformToFill"
x:Name="ImageEllipse"
SizeChanged="ImageEllipse_OnSizeChanged">
<Ellipse.Fill>
<ImageBrush ImageSource="{x:Bind ViewModel.SomePath}>
</Ellipse.Fill>
</Ellipse>
</Grid>
我正在使用 SizeChanged
事件来更新剪裁矩形的大小。
问题是这样,外部 Grid
会连同其图像一起剪切 Ellipse
,因此结果是一个缩放的圆,边缘被裁剪。
相反,我只想缩放图像 inside 椭圆。
问题是 Ellipse
控件是一个形状,所以我无法在其中添加实际的 Image
控件并改为缩放该控件。
有没有办法真正做到这一点,或者因为微软决定将剪辑 属性 限制为仅 RectangleGeometry
个对象,所以没有办法做到这一点?
使用 Border
时同样的问题,因为 CornerRadius
属性 只适用于它的背景,它实际上并没有剪裁它的内容。
谢谢!
编辑 #1 - 根据要求,这是我在缩放 Ellipse
时得到的结果:
编辑 #2 - 我试过像这样为 ImageBrush
设置动画:
<ImageBrush.Transform>
<ScaleTransform x:Name="AvatarTransform"
CenterY="0.5"
CenterX="0.5"/>
</ImageBrush.Transform>
</ImageBrush.Transform>
然后:
<Storyboard x:Name="TestAnimation">
<DoubleAnimation Storyboard.TargetName="AvatarTransform"
Storyboard.TargetProperty="ScaleY"
To="1.2" Duration="0:0:1"/>
</Storyboard>
但由于某种原因似乎没有发生任何事情,我什至不确定是否真的可以为 Brush
的 Transform
属性 设置动画,因为它是 不是一个UIElement
对象
编辑 #3 - 这是我想用动画实现的示例:
编辑 #4 - 从两个答案中,我尝试再次为 ImageBrush.Translate
属性 设置动画:
<!--Image XAML-->
<ImageBrush Stretch="Fill"
ImageSource="/SomeImage.jpg">
<ImageBrush.Transform>
<CompositeTransform x:Name="MyTransform"/>
</ImageBrush.Transform>
</ImageBrush>
<!--Image storyboard-->
<Storyboard x:Name="ScaleStory">
<DoubleAnimation Storyboard.TargetName="MyTransform"
Storyboard.TargetProperty="ScaleX"
From="1" To="1.4" Duration="0:0:1"/>
</Storyboard>
但是还是不行。另外,使用这种方法我无法设置 RenerTransformOrigin
属性,因为我没有 UIElement
可以使用,所以即使动画有效,图像也会是向右和底部缩放,这不是预期的结果。
编辑 #4 >> 已解决! - 如标记为有效的答案所示,这是我的代码:
<Ellipse Stretch="UniformToFill"
x:Name="ImageEllipse">
<Ellipse.Fill>
<ImageBrush ImageSource="/SomePath/SomeImage.jpg">
<ImageBrush.RelativeTransform>
<CompositeTransform CenterY="0.5"
CenterX="0.5"/>
</ImageBrush.RelativeTransform>
</ImageBrush>
</Ellipse.Fill>
</Ellipse>
事实证明它也适用于 Ellipse.Fill
属性,所以我不必切换到 PathGeometry
来创建剪辑图像的圆圈。
您可以将 Clip 编辑为 PathGeometry 而不是 RectangleGeometry,并且剪裁将起作用。这是一个可以放大和缩小并保持在裁剪区域内的图像示例。玩这个直到它适合你。
<Grid Width="1000"
Height="1000">
<Grid HorizontalAlignment="Center"
VerticalAlignment="Center"
Width="500"
Height="500">
<Grid.Clip>
<PathGeometry>
<PathFigure IsClosed="True"
StartPoint="0, 250">
<ArcSegment Size="1, 1"
Point="500, 250"
SweepDirection="Counterclockwise"
IsLargeArc="True" />
<ArcSegment Size="1, 1"
Point="0, 250"
SweepDirection="Counterclockwise"
IsLargeArc="True" />
</PathFigure>
</PathGeometry>
</Grid.Clip>
<Image Source="12019035.jpg"
RenderTransformOrigin="0.5,0.5">
<Image.RenderTransform>
<TransformGroup>
<ScaleTransform ScaleX="1.2"
ScaleY="1.2" />
<SkewTransform />
<RotateTransform />
<TranslateTransform />
</TransformGroup>
</Image.RenderTransform>
</Image>
</Grid>
</Grid>
我只是在一个网格内放置了一个网格,这样尺寸对于这个例子来说是正确的。
我剪裁了内部网格,使其成为使用路径的完美椭圆。随意复制该段并根据需要使用它。您只需要调整大小等以符合您自己的观点。
为了在此示例中放大和缩小图像,您需要设置动画或直接操作图像上的 ScaleTransform Scale.X 和 Scale.Y 属性,其中值是双精度值,其中 1 是100%, 1.5 150%, 等等
如果您不确定如何使用路径,我可以解释一下,但这里有示例。它基本上是 PathFigure 对象上的起点,PathFigure 内每个线段上的点是您希望遇到的下一个点。在这里,我从 0, 250 开始画一条弧线到 500, 250。然后我再画一条弧线到 0, 250 完成椭圆。它适用于这些数字,因为网格宽度/高度为 500、500。
这很酷的一点是,您可以绘制一个形状或用您想要的任何图案剪裁任何 UIElement :) 它可以是星星,或其他疯狂的形状。这就是您要找的东西,但请尽情享受吧!
我用来执行此操作的动画:将这些资源放在您的页面(XAML 侧)
<Page.Resources>
<Storyboard x:Name="StoryboardZoomIn">
<DoubleAnimationUsingKeyFrames EnableDependentAnimation="True"
Storyboard.TargetProperty="(Shape.Fill).(Brush.RelativeTransform).(CompositeTransform.ScaleX)"
Storyboard.TargetName="path">
<EasingDoubleKeyFrame KeyTime="0:0:1"
Value="1.5">
<EasingDoubleKeyFrame.EasingFunction>
<CubicEase EasingMode="EaseIn" />
</EasingDoubleKeyFrame.EasingFunction>
</EasingDoubleKeyFrame>
</DoubleAnimationUsingKeyFrames>
<DoubleAnimationUsingKeyFrames EnableDependentAnimation="True"
Storyboard.TargetProperty="(Shape.Fill).(Brush.RelativeTransform).(CompositeTransform.ScaleY)"
Storyboard.TargetName="path">
<EasingDoubleKeyFrame KeyTime="0:0:1"
Value="1.5">
<EasingDoubleKeyFrame.EasingFunction>
<CubicEase EasingMode="EaseIn" />
</EasingDoubleKeyFrame.EasingFunction>
</EasingDoubleKeyFrame>
</DoubleAnimationUsingKeyFrames>
</Storyboard>
<Storyboard x:Name="StoryboardZoomOut">
<DoubleAnimationUsingKeyFrames EnableDependentAnimation="True"
Storyboard.TargetProperty="(Shape.Fill).(Brush.RelativeTransform).(CompositeTransform.ScaleX)"
Storyboard.TargetName="path">
<EasingDoubleKeyFrame KeyTime="0:0:1"
Value="1">
<EasingDoubleKeyFrame.EasingFunction>
<CubicEase EasingMode="EaseIn" />
</EasingDoubleKeyFrame.EasingFunction>
</EasingDoubleKeyFrame>
</DoubleAnimationUsingKeyFrames>
<DoubleAnimationUsingKeyFrames EnableDependentAnimation="True"
Storyboard.TargetProperty="(Shape.Fill).(Brush.RelativeTransform).(CompositeTransform.ScaleY)"
Storyboard.TargetName="path">
<EasingDoubleKeyFrame KeyTime="0:0:1"
Value="1">
<EasingDoubleKeyFrame.EasingFunction>
<CubicEase EasingMode="EaseIn" />
</EasingDoubleKeyFrame.EasingFunction>
</EasingDoubleKeyFrame>
</DoubleAnimationUsingKeyFrames>
</Storyboard>
</Page.Resources>
现在,这是视图的其余部分。它是一个网格,两个按钮,canvas 和其中的路径。
<Grid Width="1000"
Height="1000">
<StackPanel>
<Button Content="Zoom In" Click="ButtonZoomIn_Click" />
<Button Content="Zoom Out" Click="ButtonZoomOut_Click"/>
</StackPanel>
<Canvas HorizontalAlignment="Center"
VerticalAlignment="Center"
Width="500"
Height="500"
Grid.RowSpan="2">
<Path x:Name="path">
<Path.Fill>
<ImageBrush Stretch="Fill"
ImageSource="12019035.jpg">
<ImageBrush.RelativeTransform>
<CompositeTransform CenterY="0.5"
CenterX="0.5"
ScaleX="1.5"
ScaleY="1.5" />
</ImageBrush.RelativeTransform>
</ImageBrush>
</Path.Fill>
<Path.Data>
<PathGeometry>
<PathFigure IsClosed="True"
StartPoint="0, 250">
<ArcSegment Size="1, 1"
Point="500, 250"
SweepDirection="Counterclockwise"
IsLargeArc="True" />
<ArcSegment Size="1, 1"
Point="0, 250"
SweepDirection="Counterclockwise"
IsLargeArc="True" />
</PathFigure>
</PathGeometry>
</Path.Data>
</Path>
</Canvas>
</Grid>
现在在代码隐藏中(我讨厌代码隐藏并推荐 MVVM)...
private void ButtonZoomIn_Click(object sender, RoutedEventArgs e)
=> (Resources["StoryboardZoomIn"] as Storyboard)?.Begin();
private void ButtonZoomOut_Click(object sender, RoutedEventArgs e)
=> (Resources["StoryboardZoomOut"] as Storyboard)?.Begin();
这将使图像动画化,从图像的中心点开始剪裁在椭圆内。希望这会有所帮助:)
我正在尝试找到一种方法来剪辑带有椭圆的图像,这样 如果我缩放图像,我可以保持椭圆完好无损,但到目前为止我已经运气不好。
这是我的 XAML:
<Grid>
<Grid.Clip>
<RectangleGeometry x:Name="ClippingRectangle"
Rect="0,0,200,200"/>
</Grid.Clip>
<Ellipse Stretch="UniformToFill"
x:Name="ImageEllipse"
SizeChanged="ImageEllipse_OnSizeChanged">
<Ellipse.Fill>
<ImageBrush ImageSource="{x:Bind ViewModel.SomePath}>
</Ellipse.Fill>
</Ellipse>
</Grid>
我正在使用 SizeChanged
事件来更新剪裁矩形的大小。
问题是这样,外部 Grid
会连同其图像一起剪切 Ellipse
,因此结果是一个缩放的圆,边缘被裁剪。
相反,我只想缩放图像 inside 椭圆。
问题是 Ellipse
控件是一个形状,所以我无法在其中添加实际的 Image
控件并改为缩放该控件。
有没有办法真正做到这一点,或者因为微软决定将剪辑 属性 限制为仅 RectangleGeometry
个对象,所以没有办法做到这一点?
使用 Border
时同样的问题,因为 CornerRadius
属性 只适用于它的背景,它实际上并没有剪裁它的内容。
谢谢!
编辑 #1 - 根据要求,这是我在缩放 Ellipse
时得到的结果:
编辑 #2 - 我试过像这样为 ImageBrush
设置动画:
<ImageBrush.Transform>
<ScaleTransform x:Name="AvatarTransform"
CenterY="0.5"
CenterX="0.5"/>
</ImageBrush.Transform>
</ImageBrush.Transform>
然后:
<Storyboard x:Name="TestAnimation">
<DoubleAnimation Storyboard.TargetName="AvatarTransform"
Storyboard.TargetProperty="ScaleY"
To="1.2" Duration="0:0:1"/>
</Storyboard>
但由于某种原因似乎没有发生任何事情,我什至不确定是否真的可以为 Brush
的 Transform
属性 设置动画,因为它是 不是一个UIElement
对象
编辑 #3 - 这是我想用动画实现的示例:
编辑 #4 - 从两个答案中,我尝试再次为 ImageBrush.Translate
属性 设置动画:
<!--Image XAML-->
<ImageBrush Stretch="Fill"
ImageSource="/SomeImage.jpg">
<ImageBrush.Transform>
<CompositeTransform x:Name="MyTransform"/>
</ImageBrush.Transform>
</ImageBrush>
<!--Image storyboard-->
<Storyboard x:Name="ScaleStory">
<DoubleAnimation Storyboard.TargetName="MyTransform"
Storyboard.TargetProperty="ScaleX"
From="1" To="1.4" Duration="0:0:1"/>
</Storyboard>
但是还是不行。另外,使用这种方法我无法设置 RenerTransformOrigin
属性,因为我没有 UIElement
可以使用,所以即使动画有效,图像也会是向右和底部缩放,这不是预期的结果。
编辑 #4 >> 已解决! - 如标记为有效的答案所示,这是我的代码:
<Ellipse Stretch="UniformToFill"
x:Name="ImageEllipse">
<Ellipse.Fill>
<ImageBrush ImageSource="/SomePath/SomeImage.jpg">
<ImageBrush.RelativeTransform>
<CompositeTransform CenterY="0.5"
CenterX="0.5"/>
</ImageBrush.RelativeTransform>
</ImageBrush>
</Ellipse.Fill>
</Ellipse>
事实证明它也适用于 Ellipse.Fill
属性,所以我不必切换到 PathGeometry
来创建剪辑图像的圆圈。
您可以将 Clip 编辑为 PathGeometry 而不是 RectangleGeometry,并且剪裁将起作用。这是一个可以放大和缩小并保持在裁剪区域内的图像示例。玩这个直到它适合你。
<Grid Width="1000"
Height="1000">
<Grid HorizontalAlignment="Center"
VerticalAlignment="Center"
Width="500"
Height="500">
<Grid.Clip>
<PathGeometry>
<PathFigure IsClosed="True"
StartPoint="0, 250">
<ArcSegment Size="1, 1"
Point="500, 250"
SweepDirection="Counterclockwise"
IsLargeArc="True" />
<ArcSegment Size="1, 1"
Point="0, 250"
SweepDirection="Counterclockwise"
IsLargeArc="True" />
</PathFigure>
</PathGeometry>
</Grid.Clip>
<Image Source="12019035.jpg"
RenderTransformOrigin="0.5,0.5">
<Image.RenderTransform>
<TransformGroup>
<ScaleTransform ScaleX="1.2"
ScaleY="1.2" />
<SkewTransform />
<RotateTransform />
<TranslateTransform />
</TransformGroup>
</Image.RenderTransform>
</Image>
</Grid>
</Grid>
我只是在一个网格内放置了一个网格,这样尺寸对于这个例子来说是正确的。
我剪裁了内部网格,使其成为使用路径的完美椭圆。随意复制该段并根据需要使用它。您只需要调整大小等以符合您自己的观点。
为了在此示例中放大和缩小图像,您需要设置动画或直接操作图像上的 ScaleTransform Scale.X 和 Scale.Y 属性,其中值是双精度值,其中 1 是100%, 1.5 150%, 等等
如果您不确定如何使用路径,我可以解释一下,但这里有示例。它基本上是 PathFigure 对象上的起点,PathFigure 内每个线段上的点是您希望遇到的下一个点。在这里,我从 0, 250 开始画一条弧线到 500, 250。然后我再画一条弧线到 0, 250 完成椭圆。它适用于这些数字,因为网格宽度/高度为 500、500。
这很酷的一点是,您可以绘制一个形状或用您想要的任何图案剪裁任何 UIElement :) 它可以是星星,或其他疯狂的形状。这就是您要找的东西,但请尽情享受吧!
我用来执行此操作的动画:将这些资源放在您的页面(XAML 侧)
<Page.Resources>
<Storyboard x:Name="StoryboardZoomIn">
<DoubleAnimationUsingKeyFrames EnableDependentAnimation="True"
Storyboard.TargetProperty="(Shape.Fill).(Brush.RelativeTransform).(CompositeTransform.ScaleX)"
Storyboard.TargetName="path">
<EasingDoubleKeyFrame KeyTime="0:0:1"
Value="1.5">
<EasingDoubleKeyFrame.EasingFunction>
<CubicEase EasingMode="EaseIn" />
</EasingDoubleKeyFrame.EasingFunction>
</EasingDoubleKeyFrame>
</DoubleAnimationUsingKeyFrames>
<DoubleAnimationUsingKeyFrames EnableDependentAnimation="True"
Storyboard.TargetProperty="(Shape.Fill).(Brush.RelativeTransform).(CompositeTransform.ScaleY)"
Storyboard.TargetName="path">
<EasingDoubleKeyFrame KeyTime="0:0:1"
Value="1.5">
<EasingDoubleKeyFrame.EasingFunction>
<CubicEase EasingMode="EaseIn" />
</EasingDoubleKeyFrame.EasingFunction>
</EasingDoubleKeyFrame>
</DoubleAnimationUsingKeyFrames>
</Storyboard>
<Storyboard x:Name="StoryboardZoomOut">
<DoubleAnimationUsingKeyFrames EnableDependentAnimation="True"
Storyboard.TargetProperty="(Shape.Fill).(Brush.RelativeTransform).(CompositeTransform.ScaleX)"
Storyboard.TargetName="path">
<EasingDoubleKeyFrame KeyTime="0:0:1"
Value="1">
<EasingDoubleKeyFrame.EasingFunction>
<CubicEase EasingMode="EaseIn" />
</EasingDoubleKeyFrame.EasingFunction>
</EasingDoubleKeyFrame>
</DoubleAnimationUsingKeyFrames>
<DoubleAnimationUsingKeyFrames EnableDependentAnimation="True"
Storyboard.TargetProperty="(Shape.Fill).(Brush.RelativeTransform).(CompositeTransform.ScaleY)"
Storyboard.TargetName="path">
<EasingDoubleKeyFrame KeyTime="0:0:1"
Value="1">
<EasingDoubleKeyFrame.EasingFunction>
<CubicEase EasingMode="EaseIn" />
</EasingDoubleKeyFrame.EasingFunction>
</EasingDoubleKeyFrame>
</DoubleAnimationUsingKeyFrames>
</Storyboard>
</Page.Resources>
现在,这是视图的其余部分。它是一个网格,两个按钮,canvas 和其中的路径。
<Grid Width="1000"
Height="1000">
<StackPanel>
<Button Content="Zoom In" Click="ButtonZoomIn_Click" />
<Button Content="Zoom Out" Click="ButtonZoomOut_Click"/>
</StackPanel>
<Canvas HorizontalAlignment="Center"
VerticalAlignment="Center"
Width="500"
Height="500"
Grid.RowSpan="2">
<Path x:Name="path">
<Path.Fill>
<ImageBrush Stretch="Fill"
ImageSource="12019035.jpg">
<ImageBrush.RelativeTransform>
<CompositeTransform CenterY="0.5"
CenterX="0.5"
ScaleX="1.5"
ScaleY="1.5" />
</ImageBrush.RelativeTransform>
</ImageBrush>
</Path.Fill>
<Path.Data>
<PathGeometry>
<PathFigure IsClosed="True"
StartPoint="0, 250">
<ArcSegment Size="1, 1"
Point="500, 250"
SweepDirection="Counterclockwise"
IsLargeArc="True" />
<ArcSegment Size="1, 1"
Point="0, 250"
SweepDirection="Counterclockwise"
IsLargeArc="True" />
</PathFigure>
</PathGeometry>
</Path.Data>
</Path>
</Canvas>
</Grid>
现在在代码隐藏中(我讨厌代码隐藏并推荐 MVVM)...
private void ButtonZoomIn_Click(object sender, RoutedEventArgs e)
=> (Resources["StoryboardZoomIn"] as Storyboard)?.Begin();
private void ButtonZoomOut_Click(object sender, RoutedEventArgs e)
=> (Resources["StoryboardZoomOut"] as Storyboard)?.Begin();
这将使图像动画化,从图像的中心点开始剪裁在椭圆内。希望这会有所帮助:)