在C#中以4秒的间隔在2个位置之间绘制箭头



我想在c#中制作一个箭头,例如,在5秒内从位置到B位置。我想将地图图像放在表单中,当我单击一个按钮时,我想以几秒钟的间隔将箭头从位置绘制到B位置。当它处于水平位置时,我已经制作了一个箭头,但是当我尝试使它倾斜时,它会使我绘制一个三角形,而不是箭头,我不知道该如何修复它。在这里,我从宽度为300

的位置的箭头制作了一个箭头

,我尝试用斜箭头做同样的东西,但是当我放置不同的位置时,它会使我一个三角形而不是箭头。

using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows.Forms;
using System.Drawing.Drawing2D;
namespace WindowsFormsApp4
{
    public partial class Form1 : Form
    {
        public Form1()
        {
            InitializeComponent();
            this.AutoScaleBaseSize = new System.Drawing.Size(5, 13);
            this.ClientSize = new System.Drawing.Size(400, 273);
            this.Text = "";
            this.Resize += new System.EventHandler(this.Form1_Resize);
            this.Paint += new System.Windows.Forms.PaintEventHandler(this.Form1_Paint);
        }
        private void Form1_Paint(object sender, System.Windows.Forms.PaintEventArgs e)
        {
            Graphics g = e.Graphics;
            g.SmoothingMode = SmoothingMode.AntiAlias;
            g.FillRectangle(Brushes.White, this.ClientRectangle);
            Pen p = new Pen(Color.Black, 5);
            p.StartCap = LineCap.Round;
            for(int i=1; i<=300;i++)
            {
                System.Threading.Thread.Sleep(2);
                g.DrawLine(p, 12, 30, i, 30);
                Cursor.Current = Cursors.Default;
            }
            p.EndCap = LineCap.ArrowAnchor;
            g.DrawLine(p, 12, 30, 310, 30);
            p.Dispose();
        }
        private void Form1_Resize(object sender, System.EventArgs e)
        {
            Invalidate();
        }
    }
}

代码的基本问题是您在Paint事件处理程序内执行整个动画循环。这意味着您绘制的每一行之间永远不会清除窗口,因此您可以获得您所绘制的行副本的所有查看。

从您的问题中不清楚您期望在屏幕上看到什么。但是,代码的另一个潜在问题是,该行的移动终点不是从行的起点开始,而是在同一Y坐标的点上,您希望线路结束。这意味着线的箭头末端横穿导致最终终点的水平线,而不是从线的起点逐渐延伸。

还有一个小点,您似乎对DrawLine()方法的做法感到困惑。您声明行的宽度为300,但实际上DrawLine()方法的第二个参数只是另一点。该行的宽度由您用于绘制行的Pen定义。包含行的盒子的宽度由起点和终点定义,但在这种情况下不是300,而是(在线的最后长度)中,您的start x坐标之间的差异和结束x坐标(即288)。

可以通过运行Paint事件处理程序的循环来解决上述基本问题,该 更新描述该行的值,然后调用Invalidate(),以便可以称为Paint事件处理程序绘制只是动画的当前状态

假设您真正想要的是要从起点延伸,而不是跨越水平线,我下面显示的示例也以这种方式实现了动画,此外还可以解决基本问题。我没有采取任何行动改变行的长度或宽度。

public Form1()
{
    InitializeComponent();
    this.DoubleBuffered = true;
    this.AutoScaleBaseSize = new System.Drawing.Size(5, 13);
    this.ClientSize = new System.Drawing.Size(400, 273);
    this.Resize += new System.EventHandler(this.Form1_Resize);
    this.Paint += new System.Windows.Forms.PaintEventHandler(this.Form1_Paint);
    var task = AnimateLine();
}
private readonly Point _lineStart = new Point(12, 30);
private readonly Point _lineFinalEnd = new Point(300, 60);
private const int _animateSteps = 300;
private Point _lineCurrentEnd;
private bool _drawArrow;
private async Task AnimateLine()
{
    Size size = new Size(_lineFinalEnd) - new Size(_lineStart);
    for (int i = 1; i <= _animateSteps; i++)
    {
        await Task.Delay(2);
        Size currentSize = new Size(
            size.Width * i / _animateSteps, size.Height * i / _animateSteps);
        _lineCurrentEnd = _lineStart + currentSize;
        Invalidate();
    }
    _drawArrow = true;
    Invalidate();
}
private void Form1_Paint(object sender, System.Windows.Forms.PaintEventArgs e)
{
    Graphics g = e.Graphics;
    g.SmoothingMode = SmoothingMode.AntiAlias;
    using (Pen p = new Pen(Color.Black, 5))
    {
        p.StartCap = LineCap.Round;
        p.EndCap = _drawArrow ? LineCap.ArrowAnchor : p.EndCap;
        g.DrawLine(p, _lineStart, _lineCurrentEnd);
    }
}

请注意,重复的擦除和重新刷新窗户将使窗户闪烁。这是任何一种动画的基本问题,修复程序是为窗口启用双重屏障,因此this.DoubleBuffered = true;语句添加到构造函数中。

其他值得一提的是:

  • 使用await Task.Delay()调用,以便循环可以在循环的每次迭代中产生UI线程,从而允许UI线程提高Paint事件,并允许在动画期间其他任何其他UI活动仍能工作。您可以在如何以及何时使用asyncawait文章中找到有关该C#功能的更多信息,当然,通过阅读文档。
  • 无论您使用Thread.Sleep()还是Task.Delay(),指定2 ms的延迟不是很有用。Windows线程调度程序不会将线程安排到该精度。在正常情况下,在50毫秒后,甚至在CPU承受沉重的负载下,可以唤醒2毫秒的线程。2 ms延迟也没有提供有用的动画帧速率;那将是一个500 Hz的刷新率,比人脑的需求更快或更快,以感知一个平稳的动画。

    我的上面的示例无助于解决这个问题,但是您应该以略有不同的范围探索实现循环,以便指定合理的动画间隔(例如每50或100 ms),而不是动画步骤的数量,试图延迟该间隔,然后使用Stopwatch来实际测量真正的延迟是什么,并根据经过的实际时间计算动画中的进度。这将使您能够精确控制动画的总持续时间,以及对动画使用的刷新率的精确控制。

相关内容

  • 没有找到相关文章

最新更新