Home > Software design >  Infinite scrolling gradient background
Infinite scrolling gradient background

Time:12-14

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 (the Position 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

  • Related