如何以不同的速度平滑地动画 Windows 窗体位置?

发布于 2024-07-23 09:40:06 字数 2010 浏览 6 评论 0原文

我一直在尝试平滑地为某些 Windows 窗体位置设置动画,但如果我希望速度可变,则会遇到一些问题。 换句话说,如果我想让用户选择动画的首选速度。

我发现以下文章对我为我的表单执行我正在寻找的动画有很大帮助。 它在各方面似乎都比我过去尝试过的BackgroundWorker 或Threads 方法更好: http://www.vcskicks.com/animated-windows-form.html

我现在唯一的问题是,如果我想要不同的动画速度,保持动画流畅。 我的代码中有两个重要的值:FPSPX。 FPS 表示每秒帧数(还有什么),PX 表示移动表单的像素数。

问题 1) 为了获得最流畅的动画,我希望表单一次移动 1px,但我不认为我能够像我想要的那样快地移动表单那。 将FPS值增加到很高的值似乎没有任何效果,就像有一个限制,超过这个限制,就不会有明显的差异。 我确信对此有一个很好的解释。

我的问题是:如果我想要更快的移动,您对这个问题有什么好的解决方案吗?或者唯一的解决方案是更改 PX 值并将表单移动超过 1px?

问题 2) 如果上述问题的解决方案是相应地更改 PX 值,我发现(通过测试不同的值)FPS 值等于 300 就足以满足我将表单移动为慢慢地,快地如我所愿。 然后,如果我想要 10 种速度,将表单移动 1、2、3、4、5、6、7、8、9 和 10 像素即可提供慢速和快速的平滑动画,正如我想要的那样。 例如,如果我想要 5 种速度,我可以使用 2、4、6、8、10。

我的问题是:使用像 300 这样的值作为 FPS 有什么问题吗? 这样的值有什么不好的后果吗?

这是我当前的代码:

public partial class Form1 : Form {

    bool dir = true;

    public Form1() {
        InitializeComponent();

        Location = new Point(1280/2 - Width, 800/2 - Height/2);
    }

    private void button1_Click(object sender, EventArgs e) {
        double FPS = 300;
        int PX = 1;
        long lastTicks = 0;
        long currentTicks = 0;
        double interval = (double)Stopwatch.Frequency / FPS;

        while(dir ? Left <= 1280/2 : Left >= 1280/2 - Width) {
            Application.DoEvents();

            currentTicks = Stopwatch.GetTimestamp();

            if(currentTicks >= lastTicks + interval) {
                lastTicks = Stopwatch.GetTimestamp();

                this.Location = new Point(dir ? Left + PX : Left - PX, Top);

                this.Invalidate(); //refreshes the form
            }

            Thread.Sleep(1); //frees up the cpu
        }

        dir = !dir;
    }

}

注意:这只是示例代码,用于测试目的,不是真正的代码,但如果您想指出我在将其移植到时应该考虑的一些非常重要的事情,请成为我的客人真正的应用。

I've been trying to smoothly animate some Windows Form location but I'm having some issues if I want the speed to be variable. In other words, if I want to allow the user to select the preferred speed for animation.

I've found the following article that helped me quite a bit to perform the animation I was looking for, for my form. It seems better in every way than a BackgroundWorker or Threads approach I tried in the past:
http://www.vcskicks.com/animated-windows-form.html

My only problem now, is to maintain a smooth animation if I want o have different speeds for the animation. There are two values that are important in my code, FPS and PX. FPS represents frames per second (what else) and PX represents the number of pixels to move the form.

Problem 1) To have the smoothest possible animation, I would prefer to have the form move 1px at a time but I don't think that I will be able to move the form as fast as I want like that. Increasing the FPS value to a very high value doesn't seem to take any effect, it's like there's a limit and above that limit, there will be no visible differences. I'm sure there's a good explanation for that.

My question here is: Do you have any good solution for this problem or the only solution is to change the PX value and move the form by more than 1px if I want a faster movement?

Problem 2) If the solution for the question above is to change the PX value accordingly, I found out (by testing different values) that a FPS value equal to 300 would suffice my needs to move the form as slow and as fast as I want it to. Then, if I wanted 10 speeds, moving the form by 1, 2, 3, 4, 5, 6, 7, 8, 9 and 10 pixels provides slow and fast smooth animations, just as I want it. If I wanted 5 speeds, I could use 2, 4, 6, 8, 10, for instance.

My question here is: Is there any problem to use a value like 300 for FPS? Are there any bad consequences for such a value?

And here's my current code:

public partial class Form1 : Form {

    bool dir = true;

    public Form1() {
        InitializeComponent();

        Location = new Point(1280/2 - Width, 800/2 - Height/2);
    }

    private void button1_Click(object sender, EventArgs e) {
        double FPS = 300;
        int PX = 1;
        long lastTicks = 0;
        long currentTicks = 0;
        double interval = (double)Stopwatch.Frequency / FPS;

        while(dir ? Left <= 1280/2 : Left >= 1280/2 - Width) {
            Application.DoEvents();

            currentTicks = Stopwatch.GetTimestamp();

            if(currentTicks >= lastTicks + interval) {
                lastTicks = Stopwatch.GetTimestamp();

                this.Location = new Point(dir ? Left + PX : Left - PX, Top);

                this.Invalidate(); //refreshes the form
            }

            Thread.Sleep(1); //frees up the cpu
        }

        dir = !dir;
    }

}

Note: This is just sample code, for testing purposes, not real code but be my guest if you want to point out some very important things that I should consider when porting this to the real application.

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(4

待天淡蓝洁白时 2024-07-30 09:40:06

尝试执行我之前的建议(不考虑 FPS):

public partial class Form1 : Form
{
    private bool go;
    private int dx = 1;
    public Form1()
    {
        InitializeComponent();
    }
    protected override void OnPaint(PaintEventArgs e)
    {
        base.OnPaint(e);
        if (go)
        {
            this.Location = new Point(this.Location.X + dx, this.Location.Y);
            if (Location.X < 10 || Location.X > 1200)
            {
                go = false;
                dx = -dx;
            }
            else
            {
                this.Invalidate();
            }
        }
    }
    private void button1_Click(object sender, EventArgs e)
    {
        go = true;
        this.Invalidate();
    }
}

我认为您可能必须超越 winform 才能获得更高的 FPS。

Try this implementation of my prior suggestion (without accounting for FPS):

public partial class Form1 : Form
{
    private bool go;
    private int dx = 1;
    public Form1()
    {
        InitializeComponent();
    }
    protected override void OnPaint(PaintEventArgs e)
    {
        base.OnPaint(e);
        if (go)
        {
            this.Location = new Point(this.Location.X + dx, this.Location.Y);
            if (Location.X < 10 || Location.X > 1200)
            {
                go = false;
                dx = -dx;
            }
            else
            {
                this.Invalidate();
            }
        }
    }
    private void button1_Click(object sender, EventArgs e)
    {
        go = true;
        this.Invalidate();
    }
}

I think you will likely have to go outside of winforms to get higher FPS.

深爱成瘾 2024-07-30 09:40:06

我认为 Windows 限制了重绘率,所以如果你将 FPS 调到疯狂的值也没关系; 如果您想看到更高的帧速率,您可能需要使用 XNA/DirectX 动画之类的东西。

您可以使用计时器,并编写一个既可以移动又可以移动的事件处理程序。 使您的表格无效。 为此,您不必执行 Thread.Sleep 或使用最后的刻度和间隔计算进行簿记,并且它会以定期的节奏发生。 此外,当您想要更改方向时,您可以取反 PX 值,而不是围绕“dir”布尔值的条件代码(并且仅执行加法而不是 dir 上的三元运算符); 这是可能的,因为减法与加负数相同。

这应该使您的动画更容易扩展以执行其他操作。 为了好玩,您还可以创建一个 PY 来垂直移动。 不管怎样,我希望你能从中得到一些乐趣。 :)

I think Windows limits the repaint rate, so it wouldn't matter if you cranked the FPS up to insane values; if you want to see higher frame rates you'll probably have to result to something like XNA/DirectX animation.

You could use a Timer, and write an elapsed event handler that would both move & invalidate your form. To that end you wouldn't have to do the Thread.Sleep or the book-keeping with the last ticks and interval calcs, and it would happen with a regular cadence. Additionally, instead of the conditional code around the 'dir' boolean, you could negate the PX value when you want to change directions (and do additions-only instead of the ternary operator on dir); this is possible since subtraction is the same as adding a negative.

This should make your animation easier to extend to do other things. For fun, you could also create a PY to move vertically as well. Whatever the case, I hope you have some fun with it. :)

长不大的小祸害 2024-07-30 09:40:06

如果你在 FPS 值为 300 时获得了你想要的结果,那么我会选择这个。 WinForms 的每秒帧数的概念与图形密集型视频游戏不同,因此高值不会成为问题。 这里的 FPS 值只是调整代码执行的频率。

If you got the results you want with a FPS value of 300 then I would go with that. The concept of Frames Per Second for WinForms is not the same as graphic-intensive video games, so a high value won't be a problem. The FPS value here simply adjusts how often the code is executed.

〗斷ホ乔殘χμё〖 2024-07-30 09:40:06

超级过度杀戮解决方案

摘要:制作窗口移动的动画,并播放它(也许是全屏)

详细信息:< br>
假设你的窗口是 100x100,并且是 @0,0
截取屏幕截图:(0,0)-(200,200)。
使用屏幕截图创建一个剪辑,描述将窗口从 0,0 移动到 100,100
然后创建一个无边框窗口 (0,0)-(200,200),并在其上放置一个视频播放器,并重播窗口在内部移动的动画。

不用说你的窗口将是静态的。 但您可以获得最好的动画。 您甚至可以为窗口移动添加一些效果,例如矩阵中的子弹或 Necromunga 飞船。

Super Over-Kill Solution

Summary: make an animation of your window moving, and play it back (full-screen, perhaps)

Details:
Suppose your window is 100x100, and is @0,0
Take a screen-shot of the, well, screen : (0,0)-(200,200).
Use the screen-shot to create a clip, that depicts moving your window from 0,0 to 100,100
Then create a borderless window (0,0)-(200,200), and put a video-player on it, and replay the animation of your window moving inside.

Needless to say your window will be static. But you can get the best animation there is. You can even add some effects to your window moving, like bullets in the matrix, or Necromunga ships.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文