1.创建OctopusControl类,继承Control类
2.定义定时器刷新,这里我们使用System.Timers.Timer定时器,逻辑处理与UI线程分开以便减少对UI线程的负载,如下:
_timer_Elapsed事件主要用定时生成数据供UI现场绘制图像,如下:
3.重写OnPaint方法,供UI线程绘制图像,最好用DrawRectangles绘制,如下:
到此基本完成,但是绘制动画图像我们一般需要开启双缓冲,防止动画闪烁,增加动画流畅,如下:
全部代码:
cs
public class OctopusControl : Control
{
private readonly System.Timers.Timer _timer;
public OctopusControl()
{
SetStyle(
ControlStyles.ResizeRedraw
| ControlStyles.DoubleBuffer
| ControlStyles.UserPaint
| ControlStyles.AllPaintingInWmPaint
| ControlStyles.SupportsTransparentBackColor,
true
);
this.BackColor = Color.Black;
_timer = new System.Timers.Timer();
_timer.Interval = 50;
_timer.Elapsed += _timer_Elapsed;
}
public void Start()
{
_timer.Start();
}
public void Stop() { _timer.Stop(); }
private double t = 0d;
RectangleF[] doubles=new RectangleF[200*200];
private volatile bool _isRun = false;
private void _timer_Elapsed(object? sender, System.Timers.ElapsedEventArgs e)
{
if (_isRun) return;
_isRun = true;
t += Math.PI / 120;
int j = 0;
for (int y = 100; y < 300; y++)
{
for (int x = 100; x < 300; x++)
{
var k = x / 8f - 25;
var e2 = y / 8f - 25;
var d = 5 * Math.Cos(Mag(k, e2) / 3);
var newX = (x + (d * k + k * 2 * Math.Atan(9 * Math.Cos(e2 * 2 - t / 2))) * Math.Sin(d * 2.5d - t)) / 2 + 100;
var newY = d * 13 + d * 3 * (3 + Math.Cos(d * 3 - t)) + d * e2 + 200;
doubles[j++] = new RectangleF((float)newX, (float)newY, 0.1f, 0.1f);
}
}
this.Invalidate();
}
protected override void OnPaint(PaintEventArgs e)
{
base.OnPaint(e);
Graphics g = e.Graphics;
g.Smooth();
Draw(g);
g.Smooth(false);
}
private void Draw(Graphics g)
{
if (!_isRun) return;
Stopwatch stopwatch = Stopwatch.StartNew();
g.DrawRectangles(Pens.Green, doubles);
stopwatch.Stop();
g.DrawString($"{stopwatch.ElapsedMilliseconds}ms", this.Font, Brushes.Green, 10, 10);
_isRun = false;
}
private double Mag(double x1, double y1) => Dist(0, 0, x1, y1);
private double Dist(double x1, double y1, double x2, double y2) => Math.Sqrt(Math.Pow(x1 - x2, 2) + Math.Pow(y1 - y2, 2));
protected override void Dispose(bool disposing)
{
base.Dispose(disposing);
}
}