舒缓动画'twitches'当它结束时

本文关键字:结束 舒缓 twitches 动画 | 更新日期: 2023-09-27 18:11:53

我使用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>

舒缓动画'twitches'当它结束时

由于这似乎是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上的一个属性设置:

storyboard.FillBehavior = FillBehavior.Stop;

这个FillBehavior决定当时间轴(在这个例子中是故事板)到达它的终点时会发生什么。MSDN是这样说的:

HoldEnd:当它达到活动周期结束时,时间线保持进程直到其父进程的活动和保持结束期。

Stop:当时间轴处于活动周期之外时,该时间轴停止父节点处于活动期内。

如果我们简单地将这个属性设置为FillBehavior。暂停,闪烁消失。缺点是你必须重新实现这个TriggerAction,但如果你只是想让它为双重动画工作,你可能会忽略很多。

希望这有助于任何人!

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

在花了一些时间玩ChangePropertyAction之后,我发现动画总是有闪烁,无论你选择什么缓和功能。