WPF动画:弯曲并跟随某些路径几何形状

20

好的,我正在制作一个加载屏幕并想要增加一些效果。

基本上我想做的是沿着路径几何数据来动画化一个对象...我强调“沿着”是因为我不想让一个固定的对象沿着切线移动

这是我想要实现的最佳效果:

enter image description here

我可以使用矩阵变换将这个边框元素沿着路径发送出去,但它最终会变成一个切线动画,跟随路径移动和旋转,但不能弯曲以适应路径的形状...以下是一个示例:

<Border Background="Black" BorderBrush="Transparent" Width="20" Height="20">
<Border.RenderTransform>
    <MatrixTransform x:Name="MatrixT">
        <MatrixTransform.Matrix>
            <Matrix/>
        </MatrixTransform.Matrix>
    </MatrixTransform>
</Border.RenderTransform>
<Border.Triggers>
    <EventTrigger RoutedEvent="Border.Loaded">
        <BeginStoryboard>
            <Storyboard>
                <MatrixAnimationUsingPath Storyboard.TargetName="MatrixT" Storyboard.TargetProperty="Matrix" DoesRotateWithTangent="True" Duration="0:0:5" RepeatBehavior="Forever">
                    <MatrixAnimationUsingPath.PathGeometry>
                        <PathGeometry Figures="M201.1,50.501C201.1,78.138,178.737,100.501,151.1,100.501L150.799,100.501C123.162,100.501,114.933,77.834,100.8,50.501L100.8,50.5C86.666,23.167,78.437,0.5,50.8,0.5L50.5,0.5C22.863,0.5,0.500000000000014,22.863,0.500000000000014,50.5L0.500000000000014,50.501C0.500000000000014,78.138,22.863,100.501,50.5,100.501L50.8,100.501C78.437,100.501,86.666,77.834,100.8,50.501L100.8,50.5C114.933,23.167,123.162,0.5,150.799,0.5L151.1,0.5C178.736,0.5,201.1,22.863,201.1,50.501L201.1,50.501z" PresentationOptions:Freeze="True"/>
                    </MatrixAnimationUsingPath.PathGeometry>
                </MatrixAnimationUsingPath>
            </Storyboard>
        </BeginStoryboard>
    </EventTrigger>
</Border.Triggers>

我想出了一个看起来非常出色的替代方案,但我想向社区提出这个问题,看看他们是否有任何想法来完成这个任务(或者它是否可能)......我已经在这个问题上进行了一些广泛的谷歌搜索,但没有找到有效的方法。

要求:

  1. 必须沿着路径弯曲
  2. 必须能够按比例缩放而不破坏动画效果(我看到过许多笔画动画表示形式只能在一个大小下运行,否则需要重新配置动画属性)......视口框 perfectly 可以实现此目的

如果该形状可以在尾侧锐化和淡化,那将是更大的加分项(参见上面的图像),但可能超出了可能性

编辑: 为了澄清我的“弯曲”是什么意思......我指的是下面的 B 图... A 图是我传统上看到的标准:

enter image description here


你尝试过 PathListbox 吗?http://www.microsoft.com/design/toolbox/tutorials/pathlistbox/ - JSJ
嗯...那是一个我之前没见过的有趣控件,实际上我还有几个其他用途...但最终它仍然不允许对象沿着路径弯曲/弯曲,只能沿着路径切线旋转。 - Robert Petz
这实际上是你正在寻找的,只需要做一点谷歌搜索。 - JSJ
我似乎仍然找不到任何与我正在寻找的相关的东西...请看我刚刚添加到问题中的第二张图片...PathListBox似乎允许您沿着路径放置一系列显示元素,但它不会扭曲元素的外观以弯曲沿路径(除了旋转/平移/缩放)。 - Robert Petz
2个回答

4

在WPF中将形状变形为路径非常困难。但是,可以通过同时动画化两个单独的路径,并同时动画化两个剪辑区域来实现接近的近似。

以下是XAML的近似代码。如果您仔细查看无限符号的交叉点,您会注意到在过渡期间阴影的平滑度有轻微的不连续性。这是因为我在设置LinearGradientBrush对象的Start、End和Offset点时有些任意。稍微调整一下就可以平滑过渡了。您甚至可以选择动画化刷子的属性来帮助解决这个问题。

<Window x:Class="AnimationTest.MainWindow"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        Title="MainWindow" Height="350" Width="525"
        Background="#FF486CBF">
  <Viewbox>
    <Grid>
      <Canvas Width="50" Height="50"
              HorizontalAlignment="Left"
              VerticalAlignment="Top">
        <Canvas.Clip>
          <RectangleGeometry Rect="0,0,50,55">
            <RectangleGeometry.Transform>
              <TranslateTransform x:Name="_clip1"/>
            </RectangleGeometry.Transform>
          </RectangleGeometry>
        </Canvas.Clip>
        <Path StrokeStartLineCap="Round"
              StrokeEndLineCap="Round"
              StrokeThickness="10"
              RenderTransformOrigin="0.5,0.8571"
              Data="M 5,25 c 0,-25 40,-25 40,0">
          <Path.Stroke>
            <LinearGradientBrush StartPoint="0,0" EndPoint="1,0">
              <GradientStop Color="#FFFFFFFF" Offset="0"/>
              <GradientStop Color="#00FFFFFF" Offset="0.7"/>
            </LinearGradientBrush>
          </Path.Stroke>
          <Path.RenderTransform>
            <RotateTransform x:Name="_rot1" />
          </Path.RenderTransform>
          <Path.Triggers>
            <EventTrigger RoutedEvent="Path.Loaded">
              <BeginStoryboard>
                <Storyboard>
                  <DoubleAnimation From="360" To="0"
                                   Duration="0:0:3"
                                   RepeatBehavior="Forever"
                                   Storyboard.TargetName="_rot1"
                                   Storyboard.TargetProperty="Angle"/>
                  <DoubleAnimationUsingKeyFrames Storyboard.TargetName="_clip1"
                         Storyboard.TargetProperty="Y"
                         RepeatBehavior="Forever">
                    <DiscreteDoubleKeyFrame KeyTime="0:0:0" Value="0"/>
                    <DiscreteDoubleKeyFrame KeyTime="0:0:1.5" Value="25"/>
                    <DiscreteDoubleKeyFrame KeyTime="0:0:2.8" Value="55"/>
                    <DiscreteDoubleKeyFrame KeyTime="0:0:4.5" Value="-30"/>
                    <DiscreteDoubleKeyFrame KeyTime="0:0:5.8" Value="0"/>
                    <DiscreteDoubleKeyFrame KeyTime="0:0:6" Value="0"/>
                  </DoubleAnimationUsingKeyFrames>
                </Storyboard>
              </BeginStoryboard>
            </EventTrigger>
          </Path.Triggers>
        </Path>
      </Canvas>

      <Canvas Width="50" Height="50"
              HorizontalAlignment="Left"
              VerticalAlignment="Top"
              Margin="40,0,0,0">
        <Canvas.Clip>
          <RectangleGeometry Rect="0,0,50,55">
            <RectangleGeometry.Transform>
              <TranslateTransform x:Name="_clip2"/>
            </RectangleGeometry.Transform>
          </RectangleGeometry>
        </Canvas.Clip>
        <Path StrokeStartLineCap="Round"
              StrokeEndLineCap="Round"
              StrokeThickness="10"
              RenderTransformOrigin="0.5,0.8571"
              Data="M 5,25 c 0,-25 40,-25 40,0">
          <Path.Stroke>
            <LinearGradientBrush StartPoint="1,0" EndPoint="0,0">
              <GradientStop Color="#FFFFFFFF" Offset="0"/>
              <GradientStop Color="#00FFFFFF" Offset="0.7"/>
            </LinearGradientBrush>
          </Path.Stroke>
          <Path.RenderTransform>
            <RotateTransform x:Name="_rot2" />
          </Path.RenderTransform>
          <Path.Triggers>
            <EventTrigger RoutedEvent="Path.Loaded">
              <BeginStoryboard>
                <Storyboard>
                  <DoubleAnimation From="0" To="360"
                         Duration="0:0:3"
                         RepeatBehavior="Forever"
                         Storyboard.TargetName="_rot2"
                         Storyboard.TargetProperty="Angle"/>
                  <DoubleAnimationUsingKeyFrames Storyboard.TargetName="_clip2"
                         Storyboard.TargetProperty="Y"
                         RepeatBehavior="Forever">
                    <DiscreteDoubleKeyFrame KeyTime="0:0:0" Value="55"/>
                    <DiscreteDoubleKeyFrame KeyTime="0:0:1.5" Value="-30"/>
                    <DiscreteDoubleKeyFrame KeyTime="0:0:2.8" Value="0"/>
                    <DiscreteDoubleKeyFrame KeyTime="0:0:4.5" Value="25"/>
                    <DiscreteDoubleKeyFrame KeyTime="0:0:5.8" Value="55"/>
                    <DiscreteDoubleKeyFrame KeyTime="0:0:6" Value="55"/>
                  </DoubleAnimationUsingKeyFrames>
                </Storyboard>
              </BeginStoryboard>
            </EventTrigger>
          </Path.Triggers>
        </Path>
      </Canvas>
    </Grid>
  </Viewbox>
</Window>

需要注意的重要一点是,剪切区域需要应用到Canvas对象上。如果将它们应用于Path对象,就像通常为图像所做的那样,那么剪切区域就会随着RenderTrasform旋转而旋转,这不是期望的效果。


剪裁!啊哈!那就是我完全缺失的部分...我有点想到需要稍微伪造一下动画,但我的疑虑总是因为无法使变形边缘平滑而产生...剪裁非常适合解决这个问题。+1赞为工作代码。 - Robert Petz
WPF 有变化吗?我在 <EventTrigger RoutedEvent="Path.Loaded"> 中遇到了一个错误。VS 声称 RoutedEvent 是无效的属性。 - JoshVarty
@JoshVarty 我刚刚重新加载了这个旧项目,它运行得很好。我仍然在使用Visual Studio 2012专业版。它正在运行.NET 4.0框架。 - Stewbob
嗯,我正在使用VS 2015。也许事情正在发生变化。最近一周里,有几个MSDN的WPF页面被撤下了。我会创建一个新的问题,谢谢! - JoshVarty

1
你可以在路径上放置许多圆圈并动画化它们的直径。

1
从技术上讲,这是一个答案,但并不是真正理想的...必须有更好的方法来完成这个任务...而且,除非你将其缩小到极小的规模,否则你需要放置很多圆才能看不出它不是一条平滑的线。 - Robert Petz
这取决于您认为的理想情况以及动画需要多大。创建一个“完美”的解决方案可能会过于复杂,而通过伪造也可以完全接受。 - Emond
同意,我的当前解决方案是使用一组7或8个圆形沿着路径跟随并相互追踪(彼此之间有空格),有点像你的解决方案...不过我正在寻找的是一种使用路径几何图形来变形对象的方法,这对于不仅仅是动画而言都很有用...然而,我有一种非常强烈的感觉,这在开箱即用的情况下是不可能实现的,所以如果我在接下来的几天内没有得到任何解决方案,我将把它作为问题的答案。 - Robert Petz
正确。"开箱即用"不可用。可以按照以下路径进行:http://msdn.microsoft.com/en-us/magazine/dd263097.aspx 变形是一个不同的问题:当一个段处于非常锐利的边缘时会发生什么? - Emond
是的,变形确实存在挑战,但拥有这种能力仍然很有用...锐利的边缘是一个问题,但这更多是由于功能的不当使用而不是不可能性引起的...计算样本点沿着锐利边缘的样条曲线的顶点并呈现它与呈现它沿着平滑边缘的样本点没有任何区别(仍需要完全相同的数学公式),但最终结果可能是不希望的。 - Robert Petz
显示剩余2条评论

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接