c# winforms how to rotate an image (but not around

2019-07-02 05:19发布

I am trying to develop a metronome application which I have almost completed.

The only portion I have left is a secondary visual that requires me to rotate an image of an arm (think minute hand on a clock) backwards and forwards in time.

how can I rotate an image and do so by not rotating it around its center point. in the scenario I am writing for I need to be able to rotate the image around a point in the middle of its bottom border.

Also, I would like to be able to rotate the image from X to Y in n milliseconds and have it ease in and out in the animation. Is this a bridge too far for C# and are there any libraries that could help me achieve this advance type of physics animation.

many thanks

Dan

3条回答
闹够了就滚
2楼-- · 2019-07-02 05:45

may be this sample code is useful:

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);
查看更多
该账号已被封号
3楼-- · 2019-07-02 05:46

Expanded Reply

There is no flicker whatsoever. The SetStyle in the ctor takes care of that. What you see as "flicker" is an artifact cause by three factors:

  1. The update rate is only 10/sec. Try increasing that to 20 or 30.
  2. The direction value is course. It should be based on time/accelleration. That is an exercise left to you.
  3. The crappy "hand" image has hard, aliased edges. No matter how fast you update or how smooth you animate it, it's going to look jittery. Again, doing anti-aliased, blended graphics processing is left as an exercise.

Look at the code more carefully. This is not "painted on the form", it's a custom control. See how MetronomeControl derives from Control? See how we created the form by adding a MetronomeControl to it?

Picture boxes are for displaying static images, not for custom controls!

The way it updates is by creating a timer. When the timer's Tick event fires we update the angle and direction, more generally, we update the state of the control. The call to Invalidate tells the Operating System, "Hey, I need to be repainted, send me a WM_PAINT message when it's convenient!". Our OnPaint override simply paints the current state of the control.

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",
                }
            }
        });
    }
}
查看更多
Emotional °昔
4楼-- · 2019-07-02 05:49

Not what you asked for, but:

Instead of doing a processor-intensive operation many times per second, you should consider flipping through a set of static images instead.

查看更多
登录 后发表回答