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

Posted

技术标签:

【中文标题】如何修复 WPF 控件的简单 MouseOver 动画?【英文标题】:How to fix simple MouseOver animation of a WPF control? 【发布时间】:2015-01-10 02:41:23 【问题描述】:

我正在使用触发器和情节提要制作一个简单的 MouseOver 事件。

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

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

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

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

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

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

【问题讨论】:

你能发布你的代码吗? 真的没有代码可以发布,我认为我已经很清楚地解释了这个概念。主要是由事件触发的 Blend 动画。考虑你想做我想做的事,然后看看你能不能做到,那样你会帮助我。 【参考方案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,但我找到了一个示例项目,它完全符合我的要求,没有任何特殊要求(如此处所示:s13.postimg.org/yepi4fo8n/…)这是满足我要求的示例项目的来源:codeproject.com/Articles/49802/Create-a-WPF-Custom-Control-Part 问题是我无法弄清楚为什么它适用于创建项目的人,但不适用于我自己的项目。 更具体地说,屏幕截图中描述的情节提要基本上将不透明度从 0% 设置为 0%,但不知何故,当我运行项目时,不透明度从它达到的最后一个值下降。跨度> 我已将代码更改为 Blend 行为。在这里工作。希望能帮助到你。 :-) 好的,我已经查看了您所引用的项目,它对我来说效果很好。你能用另一种方式解释这个问题,这样我可能会更好地理解它吗? 经过深入调查后,我发现我的目标应该是默认行为,但由于某种原因,它在那个特定时间点不起作用。我将动画时间延长到 2 秒,以便轻松观察关键帧如何随着时间的推移影响动画,并且它似乎可以正常工作。话虽如此,我还是要感谢您提供的支持。【参考方案2】:

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

【讨论】:

本文的示例文件 [codeproject.com/Articles/49802/Create-a-WPF-Custom-Control-Part] 完全符合我在 XAML 中的要求,但我不知道具体如何,因此我目前正在对其进行更多研究。

以上是关于如何修复 WPF 控件的简单 MouseOver 动画?的主要内容,如果未能解决你的问题,请参考以下文章

WPF中 三态图 用啥控件呢?

如何在WPF控件上应用简单的褪色透明效果?

如何在WPF Datagrids中修复“双向绑定需要路径或XPath”异常?

wpf 如何动态向tabitem添加控件

如何禁用 WPF WebBrowser 控件的点击噪音?

如果样式已经设置,如何覆盖 WPF 子控件样式?