0

I'm trying to set an animated gradient background for a JPanel. The effect works, but I would like to get a smooth transition when it starts again. Below is my current implementation. When the xvalue2 reaches a limit set, I swap the colors and start again.

public class GradientAnimation {
static class GradientPanel extends JPanel {
    private static final long serialVersionUID = -4185583782901846967L;
    private Timer timer;
    private float Xend;
    private final float MAXVALUE = 800f;
    private Color color1 = new Color(128,62,153,255);
    private Color color2 = new Color(192,201,200,255);

    GradientPanel() {
        Xend = 0f;
        setOpaque(true);
        ActionListener action = new ActionListener(){

            @Override
            public void actionPerformed(ActionEvent evt){
                if (Xend < MAXVALUE) Xend+=2f;
                else{
                    Color aux = color1;
                    color1 = color2;
                    color2 = aux;
                    Xend = 0f;
                }

                revalidate();
                repaint();
            }   
        };
        timer = new Timer(5, action);
        timer.start();
    }
    @Override
    public void paintComponent(Graphics g) {
        super.paintComponent(g);

        Graphics2D g2d = (Graphics2D) g;
        final BufferedImage image = new BufferedImage(
                getWidth(), getHeight(), BufferedImage.TYPE_INT_RGB);
        g2d = image.createGraphics();
        GradientPaint prim = new GradientPaint(0f, 0f, color1,
                Xend, 0f, color2);

        g2d.setPaint(prim);
        g2d.fillRect(0, 0, getWidth(), getHeight());

        g.drawImage(image, 0, 0, null);
    }
}

private static void createAndShowUI() {
    try {
        JFrame frame = new JFrame("Gradient Animation");
        frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
        frame.setLocationRelativeTo(null);
        frame.setResizable(false);

        GradientPanel imagePanel = new GradientPanel();

        frame.add(imagePanel);
        frame.setSize(400, 400);
        frame.setVisible(true);
    } 
    catch (Exception e) {
        e.printStackTrace();
    }
}

public static void main(String[] args) {

    SwingUtilities.invokeLater(new Runnable() {
        public void run() {
            createAndShowUI();
        }
    });
    }
}

Again, I want to hide the moment that I swap the colors to have a perfect loop of gradient animation. Please let me know if the code looks correct and how could I improve the quality.

  • Please don't post nonsense for filler as that defeats the restriction's purpose and is not playing fair with us, the volunteers who would help you. Instead please tell the details of your problem, what the code does, what it's supposed to do. – Hovercraft Full Of Eels Aug 10 '17 at 02:07
  • Yes, I understand. But my explanation was too short compared to the code, I wanted to post a working code instead of snippet so it's possible to test it out. Sorry. – Santiago Rosales Aug 10 '17 at 02:09
  • So add more explanation, not nonsense. Help us to understand what code you've written, describe it. Again, your helping us only helps you. – Hovercraft Full Of Eels Aug 10 '17 at 02:09
  • Rephrased, I did that because I couldn't post the question with the explanation above since I thought I gave enough details and a working code to try. – Santiago Rosales Aug 10 '17 at 02:20

1 Answers1

4

In the variation below,

  • Use Color.getHSBColor() to cycle through the available hues; because the hue values wrap around, the transition though the spectrum is smooth. Alternatively, change the sign of delta at the end-points.

  • Override getPreferredSize() to establish the initial panel geometry.

  • Don't buffer rendering unnecessarily.

  • Don't revalidate components unnecessarily.

image

How would I adapt it to avoid cycling through all colors?

Endless variations are possible; the critical issue is avoiding abrupt changes. Here, HUE_MIN and HUE_MAX are narrowed to a slice of the spectrum, the sign of delta is changed at the end-points, and the other HSB components get updated.

private static final float HUE_MIN = 4f/6;
private static final float HUE_MAX = 5f/6;
…
    @Override
    public void actionPerformed(ActionEvent evt) {
        hue += delta;
        if (hue > HUE_MAX) {
            hue = HUE_MAX;
            delta = -delta;
        }
        if (hue < HUE_MIN) {
            hue = HUE_MIN;
            delta = -delta;
        }
        color1 = Color.getHSBColor(hue, 1, 1);
        color2 = Color.getHSBColor(hue, 3f/4 + delta, 3f/4 + delta);
        repaint();
    }

image

Code:

import java.awt.Color;
import java.awt.Dimension;
import java.awt.GradientPaint;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.SwingUtilities;
import javax.swing.Timer;

/** @see https://stackoverflow.com/q/45603312/230513 */
public class GradientAnimation {

    static class GradientPanel extends JPanel {

        private static final int WIDE = 640;
        private static final int HIGH = 240;
        private static final float HUE_MIN = 0;
        private static final float HUE_MAX = 1;
        private final Timer timer;
        private float hue = HUE_MIN;
        private Color color1 = Color.white;
        private Color color2 = Color.black;
        private float delta = 0.01f;

        GradientPanel() {
            ActionListener action = new ActionListener() {

                @Override
                public void actionPerformed(ActionEvent evt) {
                    hue += delta;
                    if (hue > HUE_MAX) {
                        hue = HUE_MIN;
                    }
                    color1 = Color.getHSBColor(hue, 1, 1);
                    color2 = Color.getHSBColor(hue + 16 * delta, 1, 1);
                    repaint();
                }
            };
            timer = new Timer(10, action);
            timer.start();
        }

        @Override
        public void paintComponent(Graphics g) {
            super.paintComponent(g);
            Graphics2D g2d = (Graphics2D) g;
            GradientPaint p = new GradientPaint(
                0, 0, color1, getWidth(), 0, color2);
            g2d.setPaint(p);
            g2d.fillRect(0, 0, getWidth(), getHeight());
        }

        @Override
        public Dimension getPreferredSize() {
            return new Dimension(WIDE, HIGH);
        }
    }

    private static void createAndShowUI() {
        JFrame frame = new JFrame("Gradient Animation");
        frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
        GradientPanel imagePanel = new GradientPanel();
        frame.add(imagePanel);
        frame.pack();
        frame.setLocationRelativeTo(null);
        frame.setVisible(true);
    }

    public static void main(String[] args) {

        SwingUtilities.invokeLater(new Runnable() {
            public void run() {
                createAndShowUI();
            }
        });
    }
}
trashgod
  • 203,806
  • 29
  • 246
  • 1,045