简体   繁体   English

c#winforms如何旋转图像(但不是围绕其中心)

[英]c# winforms how to rotate an image (but not around its center)

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. 此外,我希望能够在n毫秒内将图像从X旋转到Y,并使其在动画中轻松进出。 Is this a bridge too far for C# and are there any libraries that could help me achieve this advance type of physics animation. 对于C#来说,这是一座过于遥远的桥梁吗?是否有任何库可以帮助我实现这种先进的物理动画类型。

many thanks 非常感谢

Dan

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. 您应该考虑翻转一组静态图像,而不是每秒多次执行处理器密集型操作。

Expanded Reply 扩大回复

There is no flicker whatsoever . 没有任何闪烁 The SetStyle in the ctor takes care of that. ctor中的SetStyle会处理这个问题。 What you see as "flicker" is an artifact cause by three factors: 你所看到的“闪烁”是由三个因素造成的神器:

  1. The update rate is only 10/sec. 更新率仅为10 /秒。 Try increasing that to 20 or 30. 尝试将其增加到20或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 ? 了解MetronomeControl如何从Control See how we created the form by adding a MetronomeControl to it? 通过添加MetronomeControl来了解我们如何创建表单?

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. 当计时器的Tick事件触发时,我们更新角度和方向,更一般地说,我们更新控件的状态。 The call to Invalidate tells the Operating System, "Hey, I need to be repainted, send me a WM_PAINT message when it's convenient!". 对Invalidate的调用告诉操作系统,“嘿,我需要重新绘制,在方便时向我发送WM_PAINT消息!”。 Our OnPaint override simply paints the current state of the control. 我们的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",
                }
            }
        });
    }
}

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);

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM