如何修复WPF控件的简单MouseOver动画?

时间:2015-01-10 02:41:24

标签: wpf expression-blend blend

我使用触发器和故事板为一个简单的MouseOver事件制作动画。

我的项目的上下文要求控件的不透明度在用户将鼠标移动到其上时增加,并在用户将鼠标移离鼠标时减少。

现在,我从30%的不透明度到100%,从100%回到30%没有问题,但我所遇到的问题是中介值/状态。

例如:如果用户将鼠标悬停在控件上,直到控件仅达到80%的不透明度,然后将鼠标移开,则不透明度降低动画从100%不透明度开始。

我怎样才能让控件从最后达到的值恢复增加/减少不透明度?

注意:我尝试了各种方法,但不值得提及,因为它们没有给出预期的结果。

编辑:显然我的目标是Blend中的默认行为,但由于某种原因它第一次没有工作。

2 个答案:

答案 0 :(得分:1)

有几个因素需要考虑。一个是如果动画没有完成,就像你提到的那样,那么下一个动画的新起始值应该从当前的不透明度开始。

其次,如果上一个动画没有完成,也应该调整动画的速度,否则下面的动画看起来会很慢。

我已为您制作了这种可附加行为,您可以为任何UIElement的不透明度设置动画。我已经在Blend和Visual Studio中对它进行了测试,它可以按照要求运行。 像这样附上:

<Button> <i:Interaction.Behaviors> <behaviors:MouseOverGlowBehavior /> </i:Interaction.Behaviors> </Button>

行为:

class MouseOverGlowBehavior : Behavior<UIElement>
{
    private const double DEFAULT_PASSIVE = 0.3d, DEFAULT_ACTIVE = 1d;

    private readonly DoubleAnimation enterAnimation, exitAnimation;

    private TimeSpan _duration = TimeSpan.FromMilliseconds(600);
    private double _span;

    public TimeSpan Duration
    {
        get { return _duration; }
        set { _duration = value; }
    }
    public double PassiveOpacity
    {
        get { return exitAnimation.To.Value; }
        set
        {
            if (exitAnimation.To.Value == value) return;

            if (value > 100) value = 100;
            else if (value < 0) value = 0;

            exitAnimation.To = value;
            _span = Math.Abs(ActiveOpacity - value);

            var target = AssociatedObject;
            if (target != null)
                target.Opacity = value;
        }
    }
    public double ActiveOpacity
    {
        get { return enterAnimation.To.Value; }
        set
        {
            if (enterAnimation.To.Value == value) return;

            if (value > 100) value = 100;
            else if (value < 0) value = 0;

            enterAnimation.To = value;
            _span = Math.Abs(value - PassiveOpacity);
        }
    }

    public MouseOverGlowBehavior()
    {
        _span = Math.Abs(DEFAULT_ACTIVE - DEFAULT_PASSIVE);
        enterAnimation = new DoubleAnimation(DEFAULT_ACTIVE, Duration);
        exitAnimation = new DoubleAnimation(DEFAULT_PASSIVE, Duration);
    }

    protected override void OnAttached()
    {
        base.OnAttached();

        AssociatedObject.Opacity = PassiveOpacity;
        AssociatedObject.MouseEnter += MouseEnter;
        AssociatedObject.MouseLeave += MouseLeave;
    }
    protected override void OnDetaching()
    {
        base.OnDetaching();

        AssociatedObject.MouseEnter -= MouseEnter;
        AssociatedObject.MouseLeave -= MouseLeave;
        AssociatedObject.Opacity = 1d;
    }

    private void MouseEnter(object sender, MouseEventArgs e)
    {
        //Increase Opacity animation
        TimeSpan duration;
        if (!CalculateDuration(ActiveOpacity, out duration)) return;
        enterAnimation.Duration = duration;

        //Start animation
        AssociatedObject.BeginAnimation(UIElement.OpacityProperty, enterAnimation);
    }
    private void MouseLeave(object sender, MouseEventArgs e)
    {
        //Decrease Opacity animation
        TimeSpan duration;
        if (!CalculateDuration(PassiveOpacity, out duration)) return;
        exitAnimation.Duration = duration;

        //Start animation
        AssociatedObject.BeginAnimation(UIElement.OpacityProperty, exitAnimation);
    }

    /// <summary>
    /// This method adjusts the adjustedDuration to take into account if previous animation did not reach it's assigned value
    /// </summary>
    /// <param name="endOpacity">Target opacity</param>
    /// <param name="adjustedDuration">Duration adjusted by current <see cref="UIElement.Opacity"/></param>
    /// <returns>True if animation is nessesary, otherwise false</returns>
    private bool CalculateDuration(double endOpacity, out TimeSpan adjustedDuration)
    {
        adjustedDuration = Duration;
        var actualSpan = Math.Abs(endOpacity - AssociatedObject.Opacity);
        if (actualSpan == 0) return false;

        var delta = actualSpan / _span;
        if (delta == 1) return true;

        adjustedDuration = TimeSpan.FromMilliseconds(Duration.TotalMilliseconds * delta);
        return true;
    }
}

答案 1 :(得分:0)

一个解决方案可能是您从代码中为对象设置动画,以便在MouseOver状态的动画运行时存储百分比值(例如80%)。 然后当光标移开时,您可以从存储的值开始减少动画。