I've searched and not found this question answered on SO, so I'm asking it here directly.
Does anyone have a clean method to create an infinitely scrolling gradient background? (the gradient shifts, so you can follow the colors from one side/corner to the other)
I've done this in VB like 15 years ago, but it's been so long since I touched VB it's all greek to me.
Assuming someone has done something like this in C# before-- Think demo scene kind of animation.
The VB code snippet is from a working form background I did many years ago, it doesn't scroll so much as bounce back and forth from edge to edge.
Private Sub picCanvas_Paint(ByVal sender As Object, ByVal e As System.Windows.Forms.PaintEventArgs) Handles Panel1.Paint
Dim rect As New Rectangle(-10, -10, Me.ClientSize.Width 20, Me.ClientSize.Height 20)
Dim halfw As Integer = CType(Me.ClientSize.Width, Integer)
Dim br As New LinearGradientBrush(New Point(-120, 500), New Point(Me.ClientSize.Width 120, 0), Color.Red, Color.Blue)
Dim color_blend As New ColorBlend
color_blend.Colors = New Color() {Color.Black, Color.Purple, Color.Teal, Color.Purple, Color.Black}
m_Theta = m_Delta
color_blend.Positions = New Single() {0, 0.01, m_Theta, 0.99, 1}
br.InterpolationColors = color_blend
e.Graphics.FillRectangle(br, rect)
br.Dispose()
If (m_Theta > 0.75) Or (m_Theta < 0.25) Then m_Delta = -m_Delta
End Sub
I would greatly appreciate any help in getting this kind of thing to work in WinForms using only GDI and brushes, no XML or anything please ^^/
CodePudding user response:
I'm not exactly sure this is what you're trying to do, anyway, from the semi-pseudo code presented here, it appears you want to shift the position of a gradient fill along an axis.
It appears the fill is meant to be inclined, so I've added means to determine a rotation angle.
I've kept the LinearGradientBrush to generate the blended fill, though the combination of GraphicsPath and PathGradientBrush is probably more flexible.
To move the gradient fill, I've used a standard System.Windows.Forms.Timer
. It's used to translate the fill, incrementing a value that is then set to the translation components of a Matrix in the OnPaint override of a double-buffered Form used as canvas (of course, you can use a PictureBox instead)
The Matrix is also used to rotate the fill, in case it's needed
The Timer's Tick
handler also verifies other conditions (bool Fields), that can be used to alter the fill:
useThetaShift
enables semi-dynamic motions of the blend intervals (thePosition
Property)useTriangular
enables and alternate blending feature, generated by the SetBlendTriangularShape() method, which considers only the starting and ending Colors of the LinearGradientBrush and defines the center point of the Colors' fall-off
The sample Form shown here can also be set to auto-scroll, the blending is extended to the DisplayRectangle
The blend is animated also when a modal Dialog is shown (you mentioned an About Window...)
internal class SomeForm : Form {
private System.Windows.Forms.Timer gradientTimer = null;
public SomeForm() {
InitializeComponent();
if (components is null) components = new Container();
ResizeRedraw = true;
startColor = blendColors[0];
meanColor = blendColors[1];
endColor = blendColors[blendColors.Length - 1];
gradientTimer = new System.Windows.Forms.Timer(components) { Interval = 100 };
gradientTimer.Tick = GradientTimer_Tick;
gradientTimer.Start();
}
float theta = .0f;
float delta = .005f;
float tringularShift = .25f;
float tringularShiftDelta = .015f;
float speed = 7.5f;
float rotation = 0f;
private Color[] blendColors = new[]{
Color.Black, Color.Purple, Color.Teal, Color.Purple, Color.Black
};
Color startColor = Color.Empty;
Color endColor = Color.Empty;
Color meanColor = Color.Empty;
PointF translateMx = PointF.Empty;
bool useThetaShift = false;
bool useTriangular = false;
private void GradientTimer_Tick(object sender, EventArgs e)
{
if (useTriangular) {
tringularShift = tringularShiftDelta;
tringularShift = Math.Max(Math.Min(tringularShift, 1.0f), .35f);
if ((tringularShift >= 1.0f) | (tringularShift <= .35f)) tringularShiftDelta*= -1;
}
if (useThetaShift) {
theta = delta;
theta = Math.Max(Math.Min(theta, .15f), 0f);
if ((theta >= .15f) | (theta <= 0f)) delta*= -1;
}
translateMx = PointF.Add(translateMx, new SizeF(speed, speed));
if (Math.Abs(translateMx.X) >= short.MaxValue) translateMx = PointF.Empty;
Invalidate();
}
protected override void OnPaint(PaintEventArgs e)
{
var display = DisplayRectangle;
using (var mx = new Matrix(1f, 0f, 0f, 1f, translateMx.X, translateMx.Y))
using (var brush = new LinearGradientBrush(display, startColor, endColor, rotation)) {
var colorBlend = new ColorBlend(blendColors.Length) {
Colors = blendColors,
Positions = new float[] { .0f, .25f theta, .5f theta, .75f theta, 1.0f },
};
brush.InterpolationColors = colorBlend;
mx.Rotate(rotation);
brush.Transform = mx;
if (useTriangular) brush.SetBlendTriangularShape(.5f, tringularShift);
e.Graphics.FillRectangle(brush, display);
}
base.OnPaint(e);
}
protected override void OnFormClosing(FormClosingEventArgs e) {
gradientTimer.Stop();
base.OnFormClosing(e);
}
}
I cannot post an animation here, because of the size. You can see how it work directly on Imgur:
Animated LinearGradientPath