5

我正在使用 Microsoft Interactivity 和 Microsoft Interactions 根据我的代码隐藏中的属性来旋转对象。为了使旋转更平滑,我添加了一个缓动函数。它的动画效果非常好,但是当它到达动画结束 1 个分割帧时,旋转会重置为动画之前的值,然后切换回旋转后的值,导致它来回“抽搐” . 这只发生在 EaseOut 上。

<i:Interaction.Triggers>
    <ie:PropertyChangedTrigger Binding="{Binding Rotation}">
        <ie:ChangePropertyAction TargetName="RotateTransformer" PropertyName="Angle" Value="{Binding Rotation}" Duration="0:0:2">
            <ie:ChangePropertyAction.Ease>                        
                <BackEase EasingMode="EaseOut" Amplitude="1.2" />
            </ie:ChangePropertyAction.Ease>
        </ie:ChangePropertyAction>
    </ie:PropertyChangedTrigger>
</i:Interaction.Triggers>
<Path Stroke="Black" Fill="Gray">
    <Path.RenderTransform>
        <RotateTransform x:Name="RotateTransformer" CenterX="64" CenterY="105" />
    </Path.RenderTransform>
    <Path.Data>
        <PathGeometry>
            <PathFigureCollection>
                <PathFigure StartPoint="64,0" >
                    <LineSegment Point="39,110" />
                    <LineSegment Point="64, 70" />
                    <LineSegment Point="39,180" />
                    <LineSegment Point="89, 180" />
                    <LineSegment Point="64,70"/>
                    <LineSegment Point="89,110" />
                    <LineSegment Point="64,0" />
                </PathFigure>
            </PathFigureCollection>
        </PathGeometry>
    </Path.Data>
</Path>
4

2 回答 2

4

由于这似乎是 ChangePropertyAction 类的实现中的一个错误,我认为了解此问题的最佳方法是将程序集放入您最喜欢的反射器样式应用程序中,并查看实现的内容。

这是一个摘录(有很多遗漏,但相关的部分在那里):

  public class ChangePropertyAction : TargetedTriggerAction<object>
  {
    /* some dependency properties here, like DurationProperty, ValueProperty, etc... */


    protected override void Invoke(object parameter)
    {
       /* a lot of validation here, but skimming over that mostly. Valid input results in a call to AnimatePropertyChange() */
    }

    private void AnimatePropertyChange(PropertyInfo propertyInfo, object fromValue, object newValue)
    {
      Storyboard storyboard = new Storyboard();
      Timeline timeline = !typeof (double).IsAssignableFrom(propertyInfo.PropertyType) 
                ? (!typeof (Color).IsAssignableFrom(propertyInfo.PropertyType)
                ? (!typeof (Point).IsAssignableFrom(propertyInfo.PropertyType)
                ? this.CreateKeyFrameAnimation(fromValue, newValue)
                    : this.CreatePointAnimation((Point) fromValue, (Point) newValue))
                    : this.CreateColorAnimation((Color) fromValue, (Color) newValue))
                    : this.CreateDoubleAnimation((double) fromValue, (double) newValue);
      timeline.Duration = this.Duration;
      storyboard.Children.Add(timeline);
      Storyboard.SetTarget((Timeline) storyboard, (DependencyObject) this.Target);
      Storyboard.SetTargetProperty((Timeline) storyboard, new PropertyPath(propertyInfo.Name, new object[0]));
      storyboard.Completed += (EventHandler) ((o, e) => propertyInfo.SetValue(this.Target, newValue, new object[0]));
      storyboard.FillBehavior = FillBehavior.Stop;
      storyboard.Begin();
    }

    private static object GetCurrentPropertyValue(object target, PropertyInfo propertyInfo)
    {
      FrameworkElement frameworkElement = target as FrameworkElement;
      target.GetType();
      object obj = propertyInfo.GetValue(target, (object[]) null);
      if (frameworkElement != null && (propertyInfo.Name == "Width" || propertyInfo.Name == "Height") && double.IsNaN((double) obj))
        obj = !(propertyInfo.Name == "Width") ? (object) frameworkElement.ActualHeight : (object) frameworkElement.ActualWidth;
      return obj;
    }

    private Timeline CreateDoubleAnimation(double fromValue, double newValue)
    {
      return (Timeline) new DoubleAnimation()
      {
        From = new double?(fromValue),
        To = new double?(newValue),
        EasingFunction = this.Ease
      };
    }
  }

如果您想查看完整代码,请自己通过 DotPeek 或 ILSpy 运行,两者都是免费的 :-)

所以最后,它所做的只是验证输入,查看值的类型并创建一个带有适合属性类型的过渡动画的情节提要。“闪烁”效果实际上是在动画完成后短暂返回其原始值(实际绑定的值)的值,之后更新绑定以反映新值。这种行为的原因归结为情节提要上的一个属性设置:

storyboard.FillBehavior = FillBehavior.Stop;

此 FillBehavior 确定时间轴(在本例中为情节提要)到达终点时会发生什么。MSDN 有这样的说法:

HoldEnd:在到达其活动期结束后,时间线会保持其进度,直到其父项的活动期和保持期结束。

停止:如果时间线在其活动期间之外而其父项在其活动期间内,则时间线将停止。

如果我们简单地将这个属性更改为 FillBehavior.HoldEnd,闪烁就消失了。缺点是您必须重新实现此 TriggerAction,但如果您只想让它用于双重动画,您可能会遗漏很多内容。

希望这对任何人都有帮助!

于 2012-08-01T19:41:42.023 回答
2

我注意到一件事,CenterX应该CenterY是小数且小于 1,例如CenterY="0.45" CenterX="0.4" 错误!

更新

玩了一段时间后ChangePropertyAction,我发现无论你选择什么缓动功能,动画都会有闪烁。

我认为这是一个错误...

于 2011-10-20T11:36:53.963 回答