在椭圆内裁剪和缩放图像(不是椭圆本身)

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>

但由于某种原因似乎没有发生任何事情,我什至不确定是否真的可以为 BrushTransform 属性 设置动画,因为它是 不是一个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();

这将使图像动画化,从图像的中心点开始剪裁在椭圆内。希望这会有所帮助:)