Java: transição suave de colors

Eu estou tentando fazer uma health bar , e como o que pode ser original, vai começar verde, e depois de perder a saúde você vai achar que vai ficar amarelo, depois laranja, depois vermelho … ou algo relativo a isso.

Eu tentei usar o método fornecido neste link: https://stackoverflow.com/questions/19841477/java-smooth-color-transition

O resultado desse link foi esse código, apenas um teste do valor 100 para 0, mas ele acabou em uma IllegalArgumentException em normalmente Red e Green , e meu palpite por motivo é que ele está acima do valor de 255.

 Color to = Color.red; Color base = Color.green; int red = (int)Math.abs((100 * to.getRed()) + ((1 - 100) * base.getRed())); int green = (int)Math.abs((100 * to.getGreen()) + ((1 - 100) * base.getGreen())); int blue = (int)Math.abs((100 * to.getBlue()) + ((1 - 100) * base.getBlue())); setForeground(new Color(red, green, blue)); 

Realmente não funcionou, e eu não tenho absolutamente nenhuma ideia de como posso fazer a transition do jeito que eu gostaria.

Então, dentro da minha class HealthBar , eu tenho um método update()

 public void update() { if (getValue() < 10) setForeground(Color.red); else if (getValue() < 25) setForeground(Color.orange); else if (getValue() < 60) setForeground(Color.yellow); else setForeground(Color.green); } 

Este código faz uma transição básica em determinados pontos.

Eu preciso criar campos para usar certas colors em determinados values da health bar , então agora eu tenho isso ..

 if (getValue() < 10) { Color to = Color.black; // Color current = getForeground() ? Color from = Color.red; // ? } 

Eu só vou usar um exemplo para o último bit.

Então eu sei que eu vou ter uma color que eu vou, e uma color que é uma base. Não tenho tanta certeza se preciso de uma color para a corrente. O problema que vejo agora é para as etapas da transição, porque cada transição tem uma quantidade diferente de etapas.

Resumo e Pergunta
Não sei como conseguir o que estou tentando, tudo o que sei com certeza é que preciso de uma color base e forneci um link para uma resposta que vi, mas não consegui entender. Com as informações fornecidas, como consegui fazer a transition colors ?

Eu passei muito tempo tentando encontrar / criar um algoritmo de mistura que funcionou para mim, isso é basicamente o que eu era capaz de mancar juntos.

Eu usei essa abordagem para gerar uma transição de gradiente misturando várias colors, como demonstrado aqui

Basicamente, essa abordagem permite que você configure uma série de colors e porcentagens para que você obtenha um controle muito maior sobre quais pontos as colors transitam entre elas.

Mistura

 import java.awt.BorderLayout; import java.awt.Color; import java.awt.Dimension; import java.awt.EventQueue; import java.awt.Graphics; import java.awt.Graphics2D; import java.text.NumberFormat; import javax.swing.JFrame; import javax.swing.JPanel; import javax.swing.JSlider; import javax.swing.UIManager; import javax.swing.UnsupportedLookAndFeelException; import javax.swing.event.ChangeEvent; import javax.swing.event.ChangeListener; public class ColorFading { public static void main(String[] args) { new ColorFading(); } public ColorFading() { EventQueue.invokeLater(new Runnable() { @Override public void run() { try { UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName()); } catch (ClassNotFoundException | InstantiationException | IllegalAccessException | UnsupportedLookAndFeelException ex) { } JFrame frame = new JFrame("Testing"); frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); frame.setLayout(new BorderLayout()); frame.add(new FadePane()); frame.pack(); frame.setLocationRelativeTo(null); frame.setVisible(true); } }); } public class FadePane extends JPanel { private final float[] fractions = new float[]{0f, 0.5f, 1f}; private final Color[] colors = new Color[]{Color.RED, Color.YELLOW, Color.GREEN}; private float progress = 1f; private JSlider slider; public FadePane() { slider = new JSlider(0, 100); setLayout(new BorderLayout()); add(slider, BorderLayout.SOUTH); slider.addChangeListener(new ChangeListener() { @Override public void stateChanged(ChangeEvent e) { progress = ((float)slider.getValue() / 100f); repaint(); } }); slider.setValue(100); } @Override public Dimension getPreferredSize() { return new Dimension(200, 200); } @Override protected void paintComponent(Graphics g) { super.paintComponent(g); Graphics2D g2d = (Graphics2D) g.create(); int width = getWidth(); int height = getHeight(); Color startColor = blendColors(fractions, colors, progress); g2d.setColor(startColor); g2d.fillRect(0, 0, width, height); g2d.dispose(); } } public static Color blendColors(float[] fractions, Color[] colors, float progress) { Color color = null; if (fractions != null) { if (colors != null) { if (fractions.length == colors.length) { int[] indicies = getFractionIndicies(fractions, progress); float[] range = new float[]{fractions[indicies[0]], fractions[indicies[1]]}; Color[] colorRange = new Color[]{colors[indicies[0]], colors[indicies[1]]}; float max = range[1] - range[0]; float value = progress - range[0]; float weight = value / max; color = blend(colorRange[0], colorRange[1], 1f - weight); } else { throw new IllegalArgumentException("Fractions and colours must have equal number of elements"); } } else { throw new IllegalArgumentException("Colours can't be null"); } } else { throw new IllegalArgumentException("Fractions can't be null"); } return color; } public static int[] getFractionIndicies(float[] fractions, float progress) { int[] range = new int[2]; int startPoint = 0; while (startPoint < fractions.length && fractions[startPoint] <= progress) { startPoint++; } if (startPoint >= fractions.length) { startPoint = fractions.length - 1; } range[0] = startPoint - 1; range[1] = startPoint; return range; } public static Color blend(Color color1, Color color2, double ratio) { float r = (float) ratio; float ir = (float) 1.0 - r; float rgb1[] = new float[3]; float rgb2[] = new float[3]; color1.getColorComponents(rgb1); color2.getColorComponents(rgb2); float red = rgb1[0] * r + rgb2[0] * ir; float green = rgb1[1] * r + rgb2[1] * ir; float blue = rgb1[2] * r + rgb2[2] * ir; if (red < 0) { red = 0; } else if (red > 255) { red = 255; } if (green < 0) { green = 0; } else if (green > 255) { green = 255; } if (blue < 0) { blue = 0; } else if (blue > 255) { blue = 255; } Color color = null; try { color = new Color(red, green, blue); } catch (IllegalArgumentException exp) { NumberFormat nf = NumberFormat.getNumberInstance(); System.out.println(nf.format(red) + "; " + nf.format(green) + "; " + nf.format(blue)); exp.printStackTrace(); } return color; } } 

OK, antes de Mad postar sua resposta (e 1+ para isso), eu estava trabalhando nisso também, então eu poderia muito bem postar o que eu inventei ….

 import java.awt.*; import java.awt.event.*; import java.beans.PropertyChangeEvent; import java.beans.PropertyChangeListener; import java.util.EnumMap; import java.util.Map; import javax.swing.*; import javax.swing.event.*; @SuppressWarnings("serial") public class ColorTransition extends JPanel { private static final int TRANSITION_DELAY = 30; private static final int PREF_W = 800; private static final int PREF_H = 600; private RgbSliderPanel rgbSliderPanel1 = new RgbSliderPanel("Color 1"); private RgbSliderPanel rgbSliderPanel2 = new RgbSliderPanel("Color 2"); private Color background1; private Color background2; private JButton button = new JButton(new ButtonAction("Push Me")); public ColorTransition() { setBackground(Color.black); add(rgbSliderPanel1.getMainPanel()); add(rgbSliderPanel2.getMainPanel()); add(button); rgbSliderPanel1.addPropertyChangeListener(new PropertyChangeListener() { @Override public void propertyChange(PropertyChangeEvent evt) { if (RgbSliderPanel.COLOR.equals(evt.getPropertyName())) { setBackground(rgbSliderPanel1.calculateColor()); } } }); } @Override public Dimension getPreferredSize() { return new Dimension(PREF_W, PREF_H); } @Override public void setEnabled(boolean enabled) { super.setEnabled(enabled); button.setEnabled(enabled); rgbSliderPanel1.setEnabled(enabled); rgbSliderPanel2.setEnabled(enabled); } private class ButtonAction extends AbstractAction { public ButtonAction(String name) { super(name); } @Override public void actionPerformed(ActionEvent e) { ColorTransition.this.setEnabled(false); background1 = rgbSliderPanel1.calculateColor(); background2 = rgbSliderPanel2.calculateColor(); setBackground(background1); Timer timer = new Timer(TRANSITION_DELAY, new TransitionListener()); timer.start(); } private class TransitionListener implements ActionListener { private int index = 0; @Override public void actionPerformed(ActionEvent e) { if (index > 100) { ((Timer) e.getSource()).stop(); ColorTransition.this.setEnabled(true); } else { int r = (int) (background2.getRed() * index / 100.0 + background1 .getRed() * (100 - index) / 100.0); int g = (int) (background2.getGreen() * index / 100.0 + background1 .getGreen() * (100 - index) / 100.0); int b = (int) (background2.getBlue() * index / 100.0 + background1 .getBlue() * (100 - index) / 100.0); setBackground(new Color(r, g, b)); } index++; } } } private static void createAndShowGui() { JFrame frame = new JFrame("ColorTransition"); frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); frame.getContentPane().add(new ColorTransition()); frame.pack(); frame.setLocationRelativeTo(null); frame.setVisible(true); } public static void main(String[] args) { SwingUtilities.invokeLater(new Runnable() { public void run() { createAndShowGui(); } }); } } enum Rgb { RED("Red"), GREEN("Green"), BLUE("Blue"); private String name; private Rgb(String name) { this.name = name; } public String getName() { return name; } } class RgbSliderPanel { public static final String COLOR = "color"; private JPanel mainPanel = new JPanel(); private SwingPropertyChangeSupport propertyChangeSupport = new SwingPropertyChangeSupport( this); private Map colorSliderMap = new EnumMap<>(Rgb.class); private String name; protected Color color; public RgbSliderPanel(String name) { this.name = name; mainPanel.setBorder(BorderFactory.createTitledBorder(name)); //mainPanel.setOpaque(false); mainPanel.setLayout(new GridLayout(0, 1)); for (Rgb rgb : Rgb.values()) { JSlider colorSlider = new JSlider(0, 255, 0); colorSliderMap.put(rgb, colorSlider); mainPanel.add(colorSlider); colorSlider.setBorder(BorderFactory.createTitledBorder(rgb.getName())); colorSlider.setPaintTicks(true); colorSlider.setPaintTrack(true); colorSlider.setMajorTickSpacing(50); colorSlider.setMinorTickSpacing(10); colorSlider.addChangeListener(new ChangeListener() { @Override public void stateChanged(ChangeEvent e) { Color oldValue = color; Color newValue = calculateColor(); color = newValue; propertyChangeSupport.firePropertyChange(COLOR, oldValue, newValue); } }); } } public JComponent getMainPanel() { return mainPanel; } public void setEnabled(boolean enabled) { for (JSlider slider : colorSliderMap.values()) { slider.setEnabled(enabled); } } public Color calculateColor() { int r = colorSliderMap.get(Rgb.RED).getValue(); int g = colorSliderMap.get(Rgb.GREEN).getValue(); int b = colorSliderMap.get(Rgb.BLUE).getValue(); return new Color(r, g, b); } public String getName() { return name; } public void addPropertyChangeListener(PropertyChangeListener listener) { propertyChangeSupport.addPropertyChangeListener(listener); } public void removePropertyChangeListener(PropertyChangeListener listener) { propertyChangeSupport.removePropertyChangeListener(listener); } }