【问题标题】:Optimizing the animation of a custom control优化自定义控件的动画
【发布时间】:2010-10-15 20:01:38
【问题描述】:

好的,所以我正在为使用 C# 的某个人创建一个“Windows 窗体应用程序”,我想让 UI 对他来说更有趣。

主窗体看起来像一个大表盘,上面排列着自定义按钮。 (我说的是自定义按钮,因为它们实际上是我创建的简单用户控件,有一个 PictureBox 和一个 Label,当用户指向它时会放大,然后在鼠标光标移到外面。还有一个 Image 属性,它设置 PictureBox 的 Image 并用作所谓的自定义按钮的图标。)

我使用了两个名为 tmrEnlargetmrShrink 的计时器,它们分别在 MouseEnterMouseLeave 事件上激活。基本上只有几个简单的函数来增加和减少在我的自定义控件中使用的 PictureBox 的大小,并使它看起来像是在放大......

它工作得很好,但问题是,当鼠标同时悬停多个控件时,动画会减慢 dows(在我看来这是正常的,因为计时器不是做我所做的事情的最佳方式!) 我也尝试过使用线程,但问题仍然存在! :-(

我想知道做这种事情的最佳方法是什么?

编辑:

这是我用于直接在控件上绘制图像而不使用 PictureBox 的代码:

(这只是一个快速版本,它在绘制图像后留下残留物,这对我来说现在并不重要)

public partial class DDAnimatingLabel : UserControl
{
    public Image Image { get; set; }

    public DDAnimatingLabel()
    {
        InitializeComponent();
    }

    private void DDAnimatingLabel_MouseEnter(object sender, EventArgs e)
    {
        tmrEnlarge.Enabled = true;
    }

    protected override void OnPaint(PaintEventArgs e)
    {
        if (Image != null)
        {
            e.Graphics.DrawImage(this.Image, e.ClipRectangle);
        }
        else
            base.OnPaint(e);
    }

    private void tmrEnlarge_Tick(object sender, EventArgs e)
    {
        if (Size.Width >= MaximumSize.Width)
        {
            tmrEnlarge.Enabled = false;
            return;
        }

        Size s = Size;
        s.Height += 4;
        s.Width += 4;
        Size = s;

        Point p = Location;
        p.X -= 2;
        p.Y -= 2;
        Location = p;
    }

    private void tmrShrink_Tick(object sender, EventArgs e)
    {
        if (tmrEnlarge.Enabled)
            return;

        if (Size.Width == MinimumSize.Width)
        {
            tmrShrink.Enabled = false;
            return;
        }

        Size s = Size;
        s.Height -= 4;
        s.Width -= 4;
        Size = s;

        Point p = Location;
        p.X += 2;
        p.Y += 2;
        Location = p;
    }

    private void DDAnimatingLabel_MouseLeave(object sender, EventArgs e)
    {
        tmrShrink.Enabled = true;
    }
}

【问题讨论】:

  • 你的开场白是矛盾的:WinForms +“更有趣一点”。正确的解决方案是为 WPF 转储 WinForms。 WPF 是为这种确切的场景而设计的。你能在 WinForms 中完成它吗?当然,您将继续遇到此类问题。如果您坚持,我建议您完全摆脱这些控件,而只是模拟它们。
  • 我想使用 WPF,但由于我不太擅长它,所以我只使用了传统的 WinForms。 “模仿”它到底是什么意思?
  • 画图不需要画框,画出来就行(OnPaint -> e.Graphics.DrawImage)。你不需要一个标签来绘制文本(e.Graphics.DrawText)。要处理输入(鼠标和键盘),您只需在表单上执行此操作,点击测试以找出单击了哪个元素或键盘的焦点标志),然后响应。
  • 啊哈!谢谢你的提示!我想知道这真的会提高性能吗?
  • 按你说的试过了,还是性能不好!当我使用这种类型的多个控件时会出现一些滞后。

标签: user-controls


【解决方案1】:

我最初误解了你的问题。前段时间我为这种东西建立了一个动画系统。在这里您可以找到该代码: http://pastebin.com/k1XmRapH.

这是我的动画系统的旧“版本”。它很粗糙,但相对较小且易于理解。下面是一个使用动画系统的演示应用。

本系统的要点是:

  • 使用单个计时器,为每个正在运行的动画提供定时“脉冲”
  • 脉冲使用经过的时间来计算要应用多少变化

演示应用程序使用 AnimationGroup 来更改 Size 和 Location 属性。当鼠标进入按钮时,我们首先取消任何现有动画,然后运行动画以增大按钮。当鼠标离开按钮时,我们再次取消任何现有动画,然后运行动画以使其恢复正常。将 AnimationTerminate.Cancel 指定为 AnimationManager.Remove 会取消正在运行的动画而不调用其 End 方法(这将完成动画)。这会使按钮的属性保持我们取消时的状态,并顺利地将按钮恢复为正常大小。

using System;
using System.Drawing;
using System.Windows.Forms;

class Form1 : Form
{
    [STAThread]
    static void Main()
    {
        Application.EnableVisualStyles();
        Application.SetCompatibleTextRenderingDefault(false);
        Application.Run(new Form1());
    }

    const int PulseInterval = 20;
    const int ButtonAnimateTime = 250;

    AnimationManager animate;
    Point[] buttonLocationsNormal, buttonLocationsHover;
    Size buttonSizeNormal, buttonSizeHover;

    public Form1()
    {
        Text = "Demo";
        ClientSize = new Size(480, 100);

        animate = new AnimationManager { PulseInterval = PulseInterval };

        buttonLocationsNormal = new Point[4];
        buttonLocationsHover = new Point[4];
        buttonSizeNormal = new Size(100, 80);
        buttonSizeHover = new Size(120, 100);

        for (int n = 0, x = 10; n < 4; n++, x += 120)
        {
            Point normalLocation = new Point(x, 10);
            buttonLocationsNormal[n] = normalLocation;
            buttonLocationsHover[n] = new Point(x - 10, 0);
            Button button = new Button { Text = "Text", Location = normalLocation, Size = buttonSizeNormal };
            button.MouseEnter += (s, e) => AnimateButton(s as Button, true);
            button.MouseLeave += (s, e) => AnimateButton(s as Button, false);
            Controls.Add(button);
        }
    }

    private void AnimateButton(Button button, bool hovering)
    {
        int index = Controls.IndexOf(button);
        AnimationGroup group = button.Tag as AnimationGroup;
        if (group != null)
            animate.Remove(group, AnimationTerminate.Cancel);

        Point newLocation;
        Size newSize;
        if (hovering)
        {
            newLocation = buttonLocationsHover[index];
            newSize = buttonSizeHover;
        }
        else
        {
            newLocation = buttonLocationsNormal[index];
            newSize = buttonSizeNormal;
        }
        group = new AnimationGroup(
            new PropertyAnimation("Location", button, newLocation, TimeSpan.FromMilliseconds(ButtonAnimateTime), new PointAnimator())
            , new PropertyAnimation("Size", button, newSize, TimeSpan.FromMilliseconds(ButtonAnimateTime), new SizeAnimator())
        );
        button.Tag = group;
        animate.Add(group);
    }
}

【讨论】:

  • 感谢您的回复。好吧,我看到您的代码是直接绘制控件,但动画效果不是我想要的! :( 我想要的是,当您将鼠标悬停时,PsuedoButton 会逐渐放大,直到达到最大尺寸,当鼠标离开组件区域时,它会逐渐缩小。只是平滑的放大/缩小过渡!我不知道我是否可以清楚地解释自己!:-)
  • 我已经用更好的答案更新了答案,现在我明白了你想要完成的事情。此动画系统确实允许使用实际控件,但如果您真的想制作优质产品,您仍然可以考虑放弃控件。
  • 我还要补充一点:如果那个粗制滥造的动画系统看起来很吓人,请记住 WPF 已经内置了一个功能齐全、出色的动画系统。=)
  • 感谢您的及时回复。但是在上面的代码中,您使用了一个名为“AnimationManager”的类。是第 3 方课程吗?
  • 我不想在这里全部发布,因为它相当长。在第一段中有一个链接。
猜你喜欢
  • 2012-08-10
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多