完成时动画'抽搐'

Per*_*rry 5 c# silverlight xaml expression-blend easing

我正在使用Microsoft Interactivity和Microsoft Interactions在我的代码隐藏中基于Property旋转对象.为了使旋转更加平滑,我添加了一个缓动功能.它可以完美地制作动画,但是当它到达动画结束的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>
Run Code Online (Sandbox Code Playgroud)

Eri*_*kel 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
      };
    }
  }
Run Code Online (Sandbox Code Playgroud)

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

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

storyboard.FillBehavior = FillBehavior.Stop;
Run Code Online (Sandbox Code Playgroud)

此 FillBehavior 确定时间轴(在本例中为故事板)到达末尾时会发生什么。MSDN 是这么说的:

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

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

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

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