65.9K
CodeProject 正在变化。 阅读更多。
Home

LinearGradientBrushAnimation

starIconstarIconstarIconstarIcon
emptyStarIcon
starIcon

4.78/5 (8投票s)

2010年3月14日

CPOL

2分钟阅读

viewsIcon

30821

downloadIcon

551

创建和使用 LinearGradientBrushAnimation。

引言

LinearGradientBrushAnimation 类似于 System.Windows.Media.Automation 中的 ColorAnimation,但它适用于 LinearGradientBrush

背景

我想重新设计按钮类,并计划在鼠标光标悬停在其上方时动画化背景。为此,我需要动画化背景画笔。我找到了 ColorAnimation 的示例,但我使用的是 LinearGradientBrush 作为背景画笔。我在网上找到了一些通过代码实现的方法,但我的计划是将此功能直接添加到 XAML 中。

LinearGradientBrushAnimationBase

首先,我使用 Reflector 工具查看,并从 AnimationTimeline 类派生了 LinearGradientBrushAnimationBase 类。我将所有 Color 类型更改为 LinearGradientBrush 类型。之后,它看起来像这样

public abstract class LinearGradientBrushAnimationBase : AnimationTimeline
{
    protected LinearGradientBrushAnimationBase()
    {
    }

    public new LinearGradientBrushAnimationBase Clone()
    {
        return (LinearGradientBrushAnimationBase)base.Clone();
    }

    public sealed override object GetCurrentValue(object defaultOriginValue, 
                  object defaultDestinationValue, AnimationClock animationClock)
    {
        if (defaultOriginValue == null)
        {
            throw new ArgumentNullException("defaultOriginValue");
        }
        if (defaultDestinationValue == null)
        {
            throw new ArgumentNullException("defaultDestinationValue");
        }
        return this.GetCurrentValue((LinearGradientBrush)defaultOriginValue, 
              (LinearGradientBrush)defaultDestinationValue, animationClock);
    }

    public LinearGradientBrush GetCurrentValue(LinearGradientBrush 
           defaultOriginValue, LinearGradientBrush defaultDestinationValue, 
           AnimationClock animationClock)
    {
        base.ReadPreamble();
        if (animationClock == null)
        {
            throw new ArgumentNullException("animationClock");
        }
        if (animationClock.CurrentState == ClockState.Stopped)
        {
            return defaultDestinationValue;
        }
        return this.GetCurrentValueCore(defaultOriginValue, 
               defaultDestinationValue, animationClock);
    }

    protected abstract LinearGradientBrush GetCurrentValueCore(LinearGradientBrush 
              defaultOriginValue, LinearGradientBrush defaultDestinationValue, 
              AnimationClock animationClock);

    // Properties
    public override Type TargetPropertyType
    {
        get { return typeof(LinearGradientBrush); }
    }
}

LinearGradientBrushAnimation

下一步是添加一个从基类派生的 LinearGradiantBrushAnimation 类。完整的类如下所示

public class LinearGradientBrushAnimation : LinearGradientBrushAnimationBase
{
    public static readonly DependencyProperty FromProperty;
    public static readonly DependencyProperty ToProperty;

    // Methods
    static LinearGradientBrushAnimation()
    {
        Type propertyType = typeof(LinearGradientBrush);
        Type ownerType = typeof(LinearGradientBrushAnimation);
        PropertyChangedCallback propertyChangedCallback = 
          new PropertyChangedCallback(
          LinearGradientBrushAnimation.AnimationFunction_Changed);
        ValidateValueCallback validateValueCallback = 
          new ValidateValueCallback(LinearGradientBrushAnimation.ValidateValues);
        FromProperty = DependencyProperty.Register("From", propertyType, 
                       ownerType, new PropertyMetadata(null, propertyChangedCallback), 
                       validateValueCallback);
        ToProperty = DependencyProperty.Register("To", propertyType, 
                     ownerType, new PropertyMetadata(null, propertyChangedCallback), 
                     validateValueCallback);
    }

    private static bool ValidateValues(object value)
    {
        return true;
    }

    private static void AnimationFunction_Changed(DependencyObject d, 
                        DependencyPropertyChangedEventArgs e)
    {
        LinearGradientBrushAnimation animation = (LinearGradientBrushAnimation)d;
        //animation.PropertyChanged(e.Property);
    }

    public LinearGradientBrushAnimation()
    {
    }

    public LinearGradientBrushAnimation(LinearGradientBrush fromValue, 
           LinearGradientBrush toValue, Duration duration)
        : this()
    {
        this.From = fromValue;
        this.To = toValue;
        base.Duration = duration;
    }

    protected override LinearGradientBrush GetCurrentValueCore(LinearGradientBrush 
              defaultOriginValue, LinearGradientBrush defaultDestinationValue, 
              AnimationClock animationClock)
    {
        // check for length of from and to
        if (From.GradientStops.Count != To.GradientStops.Count)
            return From;

        if (animationClock.CurrentProgress == null)
            return From;

        LinearGradientBrush brush = new LinearGradientBrush();
        brush.StartPoint = From.StartPoint + ((To.StartPoint - From.StartPoint) * 
                          (double)animationClock.CurrentProgress);
        brush.EndPoint = From.EndPoint + ((To.EndPoint - From.EndPoint) * 
                        (double)animationClock.CurrentProgress);

        // calc gradientstops
        for (int cnt = 0; cnt < From.GradientStops.Count; cnt++)
        {
            GradientStop stop1 = From.GradientStops[cnt];
            GradientStop stop2 = To.GradientStops[cnt];
            
            // calc color
            Color color1 = stop1.Color;
            Color color2 = stop2.Color;
            Color newColor = Color.Subtract(color2, color1);
            newColor = Color.Multiply(newColor, 
                            (float)animationClock.CurrentProgress);
            newColor = Color.Add(newColor, color1);

            // calc offset
            double offset1 = (double)stop1.Offset;
            double offset2 = (double)stop2.Offset;
            double offset = offset1 + ((offset2 - offset1) * 
                           (double)animationClock.CurrentProgress);
            
            brush.GradientStops.Add(new GradientStop(newColor, offset));
        }
        return brush;
    }

    protected override Freezable CreateInstanceCore()
    {
        return new LinearGradientBrushAnimation();
    }

    // Properties
    public LinearGradientBrush From
    {
        get { return (LinearGradientBrush)base.GetValue(FromProperty); }
        set { base.SetValue(FromProperty, value); }
    }

    public LinearGradientBrush To
    {
        get { return (LinearGradientBrush)base.GetValue(ToProperty); }
        set { base.SetValue(ToProperty, value); }
    }
}

这个类中最重要的函数是 GetCurrentValueCore()。该函数根据 animationClock.CurrentProgress 值返回动画画笔。该值是动画中的位置,是一个介于 0.0 和 1.0 之间的双精度浮点数。如你所见,我不仅动画化了 Color 值。LinearGradientBrush 的所有其他属性(StartPointEndPointOffset)也都被动画化了。

Using the Code

现在是时候测试新类了。在这个示例中,我在 XAML 中创建一个新的标签,并添加两个触发器。第一个是用于 MouseEnter,第二个是用于 MouseLeave 事件。FromTo 属性是要动画化的 LinearGradiantBrushduration 是动画持续的时间,在本示例中为 0.3 秒。如你所见,LinearGradiantBrushAnimationColorAnimation 一样直接在 XAML 中工作,并且非常易于使用。你可以将 LinearGradiantBrush 直接放入代码中,或者放入 ResourceDictionary 中,如示例所示。

<Label Content="Label 1" Margin="8" 
  HorizontalContentAlignment="Center" 
  VerticalContentAlignment="Center" 
  Height="121" Width="131" 
  Background="{StaticResource ButtonNormalBackground1}" 
  Foreground="White">
    <Label.Triggers>
        <EventTrigger RoutedEvent="Button.MouseEnter">
            <BeginStoryboard>
                <Storyboard>
                    <res:LinearGradiantBrushAnimation 
                       Storyboard.TargetProperty="Background"
                       Duration="00:00:0.3"
                       AutoReverse="False"
                       From="{StaticResource ButtonNormalBackground1}"
                       To="{StaticResource ButtonNormalBackgroundHover1}" />
                </Storyboard>
            </BeginStoryboard>
        </EventTrigger>
        <EventTrigger RoutedEvent="Button.MouseLeave">
            <BeginStoryboard>
                <Storyboard>
                    <res:LinearGradiantBrushAnimation 
                       Storyboard.TargetProperty="Background"
                       Duration="00:00:0.3"
                       AutoReverse="False"
                       From="{StaticResource ButtonNormalBackgroundHover1}"
                       To="{StaticResource ButtonNormalBackground1}" />
                </Storyboard>
            </BeginStoryboard>
        </EventTrigger>
    </Label.Triggers>
</Label>

在完整的示例中,有四个带有不同动画的 Label

历史

  • 2010年3月14日:初始发布
© . All rights reserved.