【问题标题】:Clipping and scaling an image within an ellipse (not the ellipse itself)在椭圆内裁剪和缩放图像(不是椭圆本身)
【发布时间】:2017-06-14 01:44:44
【问题描述】:

我正在尝试找到一种方法来剪辑带有椭圆的图像,以便如果我缩放图像,我会保持椭圆完整,但到目前为止我还没有运气。

这是我的 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,连同它的图像,所以结果是一个带有裁剪边缘的缩放圆。 相反,我只想在椭圆内部缩放图像。

问题在于Ellipse 控件是一个形状,因此我无法在其中添加实际的Image 控件并改为缩放该控件。 有没有办法真正做到这一点,或者由于微软决定将剪辑属性限制为 RectangleGeometry 对象,所以没有办法做到这一点?

使用Border 时会出现同样的问题,因为CornerRadius 属性仅适用于其背景,实际上并不会剪辑其内容。

谢谢!

EDIT #1 - 根据要求,这是我在缩放 Ellipse 时得到的结果:

EDIT #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 对象

EDIT #3 - 这是我想通过动画实现的示例:

EDIT #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 来创建剪辑图像的圆圈。

【问题讨论】:

  • 你能提供屏幕实际结果吗?
  • 你不能只使用ImageBrush.Transform 来转换图像吗?
  • @AndriiKrupka 我已经用请求的图片更新了问题
  • @DecadeMoon 我为动画使用 UI.Composition 命名空间,所以我没有考虑到这一点。我尝试使用简单的 DoubleAnimation,但仍然没有运气(请参阅更新的问题)
  • 提供一些具有预期结果的图像。很难理解你在 final/ 中想要什么

标签: c# xaml windows-runtime uwp windows-10-universal


【解决方案1】:

我用来执行此操作的动画:将这些资源放在您的页面中(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>

现在,这是视图的其余部分。它是一个网格、两个按钮、画布和路径。

   <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();

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

【讨论】:

  • 再次抱歉,但这对我不起作用,看起来 ImageBrush.Transform 上的动画不起作用。至少,我在 XAML 和后面的代码中都尝试了两个 Storyboard,但它们没有做任何事情。另外,使用这种方法无法设置 RenderTransformOrigin,因此即使动画确实有效,图像最终也会向右而不是在中心缩放。我开始认为这在 UWP 上是不可行的,至少在 XAML 中是不可行的......
  • 我发送了整个代码。如果您对此感到满意,请标记为已回答。
  • 我当然会,只是让我测试一下(现在是凌晨 3 点,假设是明天),所以我可以确认它确实回答了我的问题 - 不过这次看起来确实如此。伙计,你真的很敬业,谢谢! :)
  • 完美运行!我还能够继续使用 Ellipse,并使用您的 Storyboard 为内部图像设置动画,我不必切换到 PathGeometry,结果比预期的还要好,非常感谢!
  • 哦,废话,哈哈。我什至没有尝试过你的椭圆。我只是假设它没有被剪辑大声笑。好工作的人。现在我打自己的脑袋是有道理的,因为椭圆实际上只是或多或少已经内置的那些路径的形状。
【解决方案2】:

您可以将 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 :) 它可以是星形,也可以是其他一些疯狂的形状。这就是您正在寻找的东西,但玩得开心!

【讨论】:

  • 对不起,但正如我在问题中所说,Clip 属性值只能是 WinRT 上的 RectangleGeometry 对象,因此此方法不适用于 UWP
  • 你是对的……那太臭了。我会继续挖掘,因为一定有办法。
猜你喜欢
  • 2012-06-20
  • 2014-05-01
  • 1970-01-01
  • 1970-01-01
  • 2011-09-19
  • 2017-07-05
  • 1970-01-01
  • 1970-01-01
  • 2012-04-21
相关资源
最近更新 更多