C# WinForms 如何旋转图像(但不围绕其中心旋转)

本文关键字:旋转 WinForms 图像 何旋转 | 更新日期: 2023-09-27 18:36:40

我正在尝试开发一个节拍器应用程序,我几乎已经完成了。

我唯一剩下的部分是辅助视觉,它要求我在时间上前后旋转手臂的图像(想想时钟上的分针)。

如何旋转图像并通过不围绕其中心点旋转图像来实现。 在我正在编写的场景中,我需要能够围绕其底部边框中间的点旋转图像。

另外,我希望能够在n毫秒内将图像从X旋转到Y,并在动画中轻松进出。 对于 C# 来说,这是一个太远的桥梁,是否有任何库可以帮助我实现这种高级类型的物理动画。

非常感谢

C# WinForms 如何旋转图像(但不围绕其中心旋转)

扩展回复

没有任何闪烁。ctor中的SetStyle负责解决这个问题。您看到的"闪烁"是由三个因素引起的伪影:

  1. 更新速率仅为 10/秒。请尝试将其增加到 20 或 30。
  2. 方向值为航向。它应该基于时间/加速度。这是留给你们的练习。
  3. 蹩脚的"手"图像具有坚硬的锯齿边缘。无论您更新的速度有多快或动画效果有多流畅,它看起来都会抖动。同样,执行抗锯齿、混合图形处理将作为练习。

更仔细地查看代码。这不是"绘制在窗体上",而是一个自定义控件。看看MetronomeControl是如何从Control衍生而来的?看看我们如何通过添加节拍器控件来创建表单?

图片框用于显示静态图像,而不是用于自定义控件!

它的更新方式是创建一个计时器。当计时器的 Tick 事件触发时,我们更新角度和方向,更一般地说,我们更新控件的状态。对 Invalidate 的调用告诉操作系统,"嘿,我需要重新粉刷,在方便的时候给我发一条WM_PAINT消息!我们的 OnPaint 重写只是绘制控件的当前状态。

using System;
using System.Drawing;
using System.Drawing.Imaging;
using System.Windows.Forms;
class MetronomeControl : Control
{
    private Bitmap hand;
    private float angle = 0;
    private float direction = 2;
    private Timer timer = new Timer { Enabled = true, Interval = 30 };
    public MetronomeControl()
    {
        SetStyle(ControlStyles.OptimizedDoubleBuffer | ControlStyles.UserPaint | ControlStyles.Opaque | ControlStyles.AllPaintingInWmPaint, true);
        hand = CreateCrappyHandBitmap();
        timer.Tick += new EventHandler(timer_Tick);
    }
    void timer_Tick(object sender, EventArgs e)
    {
        if (angle < -45 || angle > 45)
            direction = -direction;
        angle += direction;
        Invalidate();
    }
    private static Bitmap CreateCrappyHandBitmap()
    {
        Bitmap bitmap = new Bitmap(100, 300, PixelFormat.Format32bppArgb);
        using (Graphics graphics = Graphics.FromImage(bitmap))
        {
            graphics.Clear(Color.Transparent);
            graphics.FillRectangle(Brushes.LightGray, 50 - 5, 0, 10, 300);
            graphics.FillPolygon(Brushes.LightSlateGray, new Point[] { new Point(50 - 30, 40), new Point(50 + 30, 40), new Point(50 + 20, 80), new Point(50 - 20, 80) });
            graphics.FillEllipse(Brushes.LightSlateGray, 0, 200, 100, 100);
        }
        return bitmap;
    }
    protected override void OnPaint(PaintEventArgs e)
    {
        // Erase background since we specified AllPaintingInWmPaint
        e.Graphics.Clear(Color.AliceBlue);
        e.Graphics.DrawString(Text, Font, Brushes.Black, new RectangleF(0, 0, ClientSize.Width, ClientSize.Height), new StringFormat { Alignment = StringAlignment.Center, LineAlignment = StringAlignment.Center });
        // Move the 0,0 point to the just below the bottom-center of our client area
        e.Graphics.TranslateTransform(ClientSize.Width / 2, ClientSize.Height + 40);
        // Rotate around this new 0,0
        e.Graphics.RotateTransform(angle);
        // Turn on AA to make it a bit less jagged looking
        e.Graphics.SmoothingMode = System.Drawing.Drawing2D.SmoothingMode.AntiAlias;
        // Draw the image so that the center of the ellipse is at 0,0
        e.Graphics.DrawImage(hand, 0 - hand.Width / 2, 0 - hand.Height + 50);
        // Reset the transform for other drawing
        e.Graphics.ResetTransform();
        base.OnPaint(e);
    }
}
static class Program
{
    [STAThread]
    static void Main()
    {
        Application.EnableVisualStyles();
        Application.SetCompatibleTextRenderingDefault(false);
        Application.Run(new Form
        {
            Text = "Metronome Control Demo",
            ClientSize = new Size(640, 480),
            Controls =
            {
                new MetronomeControl
                {
                    Location = new Point(10, 10),
                    Size = new Size (340, 300),
                    Font = new Font(FontFamily.GenericSansSerif, 24),
                    Text = "Metronome Control Demo",
                }
            }
        });
    }
}

不是你要求的,而是:

与其每秒多次执行处理器密集型操作,不如考虑翻阅一组静态图像。

可能是

这个示例代码很有用:

Graphics x=Graphics.FromImage(m);
x.TranslateTransform(m.Width / 2, m.Height / 2);
x.RotateTransform(30);
SizeF textSize = x.MeasureString("hi", font);
x.DrawString("hi", font, Brushes.Red, -(textSize.Width / 2), -(textSize.Height / 2);