Я добавил на форму таймер (Interval=100, Enabled=true), потом написал вот этот код: using System using System.Collections.Generic using System.Drawing using System.Linq using System.Windows.Forms namespace LoveWins { public partial class RainbowForm : Form { static List RainbowColors = new List { Color.FromArgb(0xE4, 0x03, 0x03), Color.FromArgb(0xFF, 0x8C, 0x00), Color.FromArgb(0xFF, 0xED, 0x00), Color.FromArgb(0x00, 0x80, 0x26), Color.FromArgb(0x00, 0x4D, 0xFF), Color.FromArgb(0x75, 0x07, 0x87), } static List RainbowBrushes = RainbowColors.Select(c => new SolidBrush(c)).ToList() float angle = 0 public RainbowForm () { InitializeComponent() } void lgbtFlagTimer_Tick (object sender, EventArgs e) { using (Graphics graphics = CreateGraphics()) { graphics.FillRectangle(Brushes.White, ClientRectangle) graphics.RotateTransform(angle) angle += 1f for (int i = 0 i < RainbowBrushes.Count i++) graphics.FillRectangle(RainbowBrushes[i], 0, i * 80, 777, 80) } } } } У меня флаг поворачивается, но это совсем не то, что надо. Никак не пойму, как сделать, чтобы флаг развевался волнами, как обычно флаги рисуют. У Graphics есть всякие RotateTransform и ScaleTransform, но как сделать из этого волну непонятно. Надо примерно так:
Ещё флаг мерцает почему-то...
Ответ Вот моя первая попытка со сплайнами. Выглядит не очень, но я попробую улучшить. Корневая часть такова: void NewPosition() { firstPositionTarget = new PointF((float)(rnd.NextDouble() * 100 + 50), (float)(rnd.NextDouble() * 50 - 25)) positions.Insert(0, new PointF(0f, 0f)) } void UpdatePositions() { for (int i = positions.Count - 1 i >= 0 i--) { var x = positions[i].X + 10 if (x >= ClientSize.Width + 200) { positions.RemoveAt(i) } else if (i > 0) { positions[i] = new PointF(x, positions[i].Y) } else if (x < firstPositionTarget.X) { positions[i] = new PointF(x, firstPositionTarget.Y * (x / firstPositionTarget.X)) } else { positions[i] = new PointF(x, firstPositionTarget.Y) // add new point NewPosition() } } if (positions.Count == 0 || positions.Max(p => p.X) < ClientSize.Width) positions.Add(new PointF(ClientSize.Width + 200, 0)) } Это вычисление позиций опорных точек сплайна. По сути, новая точка покамест выбирается случайно, из-за этого картинка не очень правдоподобна. Отрисовка: void germanFlagTimer_Tick(object sender, EventArgs e) { UpdatePositions() using (Graphics graphics = Graphics.FromImage(offscreenBitmap)) { graphics.SmoothingMode = SmoothingMode.HighQuality graphics.FillRectangle(Brushes.White, ClientRectangle) var totalPositions = Enumerable.Range(1, GermanBrushes.Count + 1) .Select(n => n * WaveHeight) .Select(y => new[] { new PointF(0, y) } .Concat(positions.Select(p => new PointF(p.X, p.Y + y))) .ToArray()) .ToList() for (int i = 0 i < GermanBrushes.Count i++) { GraphicsPath path = new GraphicsPath() path.AddCurve(totalPositions[i], tension: 0.5f) path.AddLine(totalPositions[i].Last(), totalPositions[i + 1].Last()) path.AddCurve(totalPositions[i+1].Reverse().ToArray(), tension: 0.5f) path.AddLine(totalPositions[i+1].First(), totalPositions[i].First()) graphics.FillPath(GermanBrushes[i], path) } using (Graphics formGraphics = CreateGraphics()) formGraphics.DrawImage(offscreenBitmap, ClientRectangle) } } Напоминаю, что для радужных цветов достаточно изменить наполнение массива с цветами. Полный код: public partial class GermanForm : Form { const int WaveHeight = 80 const int LineHeight = 80 static List GermanColors = new List { Color.FromArgb(0x0A, 0x0A, 0x0D), Color.FromArgb(0xC1, 0x12, 0x1C), Color.FromArgb(0xEE, 0xC9, 0x00) } static List GermanBrushes = GermanColors.Select(c => new SolidBrush(c)).ToList() Bitmap offscreenBitmap public GermanForm() { InitializeComponent() ClientSize = new Size(777, LineHeight * GermanColors.Count + WaveHeight * 2) offscreenBitmap = new Bitmap(ClientSize.Width, ClientSize.Height) NewPosition() } List positions = new List() PointF firstPositionTarget Random rnd = new Random() void NewPosition() { firstPositionTarget = new PointF((float)(rnd.NextDouble() * 100 + 50), (float)(rnd.NextDouble() * 50 - 25)) positions.Insert(0, new PointF(0f, 0f)) } const float step = 10 void UpdatePositions() { for (int i = positions.Count - 1 i >= 0 i--) { var x = positions[i].X + step if (x >= ClientSize.Width + 200) positions.RemoveAt(i) else { if (i > 0) { positions[i] = new PointF(x, positions[i].Y) } else if (x < firstPositionTarget.X) { positions[i] = new PointF(x, firstPositionTarget.Y * (x / firstPositionTarget.X)) } else { positions[i] = new PointF(x, firstPositionTarget.Y) // add new point NewPosition() } } } if (positions.Count == 0 || positions.Max(p => p.X) < ClientSize.Width) positions.Add(new PointF(ClientSize.Width + 200, 0)) } void germanFlagTimer_Tick(object sender, EventArgs e) { UpdatePositions() using (Graphics graphics = Graphics.FromImage(offscreenBitmap)) { graphics.SmoothingMode = SmoothingMode.HighQuality graphics.FillRectangle(Brushes.White, ClientRectangle) var totalPositions = Enumerable.Range(1, GermanBrushes.Count + 1) .Select(n => n * WaveHeight) .Select(y => new[] { new PointF(0, y) } .Concat(positions.Select(p => new PointF(p.X, p.Y + y))) .ToArray()) .ToList() for (int i = 0 i < GermanBrushes.Count i++) { GraphicsPath path = new GraphicsPath() path.AddCurve(totalPositions[i], tension: 0.5f) path.AddLine(totalPositions[i].Last(), totalPositions[i + 1].Last()) path.AddCurve(totalPositions[i+1].Reverse().ToArray(), tension: 0.5f) path.AddLine(totalPositions[i+1].First(), totalPositions[i].First()) graphics.FillPath(GermanBrushes[i], path) } using (Graphics formGraphics = CreateGraphics()) formGraphics.DrawImage(offscreenBitmap, ClientRectangle) } } }