Java: Smooth Color Transition

Java Smooth Color Transition

You're using the wrong sign in the calcuations. Should be plus, not minus, to apply the ratio properly.

int red = (int)Math.abs((ratio * FAR.getRed()) + ((1 - ratio) * CLOSE.getRed()));
int green = (int)Math.abs((ratio * FAR.getGreen()) + ((1 - ratio) * CLOSE.getGreen()));
int blue = (int)Math.abs((ratio * FAR.getBlue()) + ((1 - ratio) * CLOSE.getBlue()));

The reason you are getting dark colours with your existing implementation is that with (-), they would often fall close to zero (less than 50? or negative but greater than -50?) and in the negative case, well, you are taking the absolute value so it becomes a small positive number, i.e. a dark colour.

Java: Smooth Color Transition

I spent a lot of time trying to find/create a blending algorithm that worked for me, this is basically what I was able to hobble together.

I've used this approach to generate a gradient transition mixing multiple colors, as demonstrated here

Basically, this approach allows you to set up a series of colors and percentage marks so that you gain much greater control over which points the colors transition between.

Blend

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;
}
}

Is there a way to smooth out the transition of a graphics component from one point to another using Timer class in Java?

Okay, I found that by decreasing the delay value when initializing the Timer object I can speed up the repaint process here t = new Timer(500, new TimerListener()); by changing 500 to something like 50. Decreasing this value will speed up the animation.

And the smoothing can be implemented by decreasing the increment value inside the actionPerformed() method of the TimeListener class here targetPos.setLocation(targetPos.getX()+15, targetPos.getY());by changing the increment value 15 to something like 3. Decreasing this value will help smooth the animation.

Create smooth gradient with HSB in java

Thanks @Thomas. Turns out all three of the values for Color.getHSBColor() range from 0-1, and values higher than that produce ~undefined behaviour~.

Change JButton Text and BackGround Color with Smooth Look

Hopefully I understood your problem. What you want is animation. To do this you need to use a timer.

The following code demonstrates. Note that I changed the class name to MyPanel so as not to clash with class java.awt.Panel.

import java.awt.Color;
import java.awt.Dimension;
import java.awt.GridLayout;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;

import javax.swing.JButton;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.SwingUtilities;
import javax.swing.Timer;

public class MyPanel extends JPanel implements ActionListener {
JButton[] button = new JButton[4];
boolean flag = true;
Timer timer;
int currentIndex;
int delta;

public MyPanel() {
this.setLayout(new GridLayout(1, 4));
for (int i = 0; i < 4; ++i) {
button[i] = new JButton("");
button[i].setBackground(Color.WHITE);
button[i].addActionListener(this);
button[i].setPreferredSize(new Dimension(100, 100));
add(button[i]);
}
button[0].setText("2");
button[0].setBackground(Color.GREEN);
timer = new Timer(500, event -> {
button[currentIndex].setText("");
button[currentIndex].setBackground(Color.WHITE);
currentIndex += delta;
button[currentIndex].setText("2");
button[currentIndex].setBackground(Color.GREEN);
if (currentIndex == 0 || currentIndex == 3) {
timer.stop();
}
});
timer.setInitialDelay(0);
}

@Override
public void actionPerformed(ActionEvent e) {
if (flag) {
flag = false;
delta = 1;
}
else {
flag = true;
delta = -1;
}
timer.start();
}

public static void main(String[] args) {
SwingUtilities.invokeLater(() -> {
JFrame jFrame = new JFrame("Grid");
jFrame.add(new MyPanel());
jFrame.pack();
jFrame.setResizable(false);
jFrame.setVisible(true);
});
}
}

How to make animations on swing faster and smooth

I cannot find the way to make a fast and smooth color transition, either smooth but slow or fast but laggy, how can I do it?

It looks to me like you have two timers, one to change the time and one to change the gradient.

However, the code you posted for the second timer doesn't really do anything. All you do is set the value of some variables. Nowhere do you ever do any repainting of the component with the new variables so the gradient only changes every second when the time changes.

So I would suggest that you need to add a repaint() to the ActionListener of the second timer. This will allow gradient changes to occur at the interval you specify in addition to painting the time every second.

Background color transition on a JavaFX 8 Node/Region/Pane

Best way would be to provide a custom animation, like this (elaborating on fabian's answer):

@Override
public void start(Stage primaryStage) {

Label label = new Label("Bla bla bla bla");

Button btn = new Button("flash");
VBox box = new VBox(10, label, btn);
box.setPadding(new Insets(10));

btn.setOnAction((ActionEvent event) -> {

//**************************
//this animation changes the background color
//of the VBox from red with opacity=1
//to red with opacity=0
//**************************
final Animation animation = new Transition() {

{
setCycleDuration(Duration.millis(1000));
setInterpolator(Interpolator.EASE_OUT);
}

@Override
protected void interpolate(double frac) {
Color vColor = new Color(1, 0, 0, 1 - frac);
box.setBackground(new Background(new BackgroundFill(vColor, CornerRadii.EMPTY, Insets.EMPTY)));
}
};
animation.play();

});

Scene scene = new Scene(box, 100, 100);

primaryStage.setScene(scene);
primaryStage.show();

}

Smooth gradient background animation java

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();
}
});
}
}


Related Topics



Leave a reply



Submit