如何慢慢地将物体颜色从一个变为另一个?

我试图实现一个对象的颜色从一种颜色到另一种颜色缓慢变化的场景。

我的初始颜色为targetColor,最终颜色为updateColor。 changingSpeed变量设置为5。

我必须使用的机制是

  1. 使用getRed()getGreen()getBlue()来获取红色,绿色和蓝色
  2. 计算目标颜色的差异bytargetColor-color = [dr dg db]
  3. 通过除以向量[dr dg db] T(小心div为零)的范数来标准化[dr dg db]
  4. 通过changeSpeed将其乘以控制改变颜色的速度
  5. 将颜色更新为颜色+ [dr’dg’db’]

到目前为止,我已经能够制作以下代码:

 dr=targetColor.getRed()-updateColor.getRed(); dg=targetColor.getGreen()-updateColor.getGreen(); db=targetColor.getBlue()-updateColor.getBlue(); double nrml= Math.sqrt((dr*dr)+(dg*dg)+(db*db)); dr=dr/nrml; dg=dg/nrml; db=db/nrml; 

如何执行第4步和第5步? 可以请任何人通过代码示例指定如何做到这一点? 另请检查以上代码是否正确。

下面是一个示例,当您从一个组件移动到另一个组件时,淡化背景:

 import java.awt.*; import java.awt.event.*; import java.util.Hashtable; import java.util.ArrayList; import javax.swing.*; public class Fader { // background color when component has focus private Color fadeColor; // steps to fade from original background to fade background private int steps; // apply transition colors at this time interval private int interval; // store transition colors from orginal background to fade background private Hashtable backgroundColors = new Hashtable(); /* * Fade from a background color to the specified color using * the default of 10 steps at a 50 millisecond interval. * * @param fadeColor the temporary background color */ public Fader(Color fadeColor) { this(fadeColor, 10, 50); } /* * Fade from a background color to the specified color in the * specified number of steps at the default 5 millisecond interval. * * @param fadeColor the temporary background color * @param steps the number of steps to fade in the color */ public Fader(Color fadeColor, int steps) { this(fadeColor, steps, 50); } /* * Fade from a background color to the specified color in the * specified number of steps at the specified time interval. * * @param fadeColor the temporary background color * @param steps the number of steps to fade in the color * @param intevral the interval to apply color fading */ public Fader(Color fadeColor, int steps, int interval) { this.fadeColor = fadeColor; this.steps = steps; this.interval = interval; } /* * Add a component to this fader. * * The fade color will be applied when the component gains focus. * The background color will be restored when the component loses focus. * * @param component apply fading to this component */ public Fader add(JComponent component) { // Get colors to be used for fading ArrayList colors = getColors( component.getBackground() ); // FaderTimer will apply colors to the component new FaderTimer( colors, component, interval ); return this; } /* ** Get the colors used to fade this background */ private ArrayList getColors(Color background) { // Check if the color ArrayList already exists Object o = backgroundColors.get( background ); if (o != null) { return (ArrayList)o; } // Doesn't exist, create fader colors for this background ArrayList colors = new ArrayList( steps + 1 ); colors.add( background ); int rDelta = ( background.getRed() - fadeColor.getRed() ) / steps; int gDelta = ( background.getGreen() - fadeColor.getGreen() ) / steps; int bDelta = ( background.getBlue() - fadeColor.getBlue() ) / steps; for (int i = 1; i < steps; i++) { int rValue = background.getRed() - (i * rDelta); int gValue = background.getGreen() - (i * gDelta); int bValue = background.getBlue() - (i * bDelta); colors.add( new Color(rValue, gValue, bValue) ); } colors.add( fadeColor ); backgroundColors.put(background, colors); return colors; } class FaderTimer implements FocusListener, ActionListener { private ArrayList colors; private JComponent component; private Timer timer; private int alpha; private int increment; FaderTimer(ArrayList colors, JComponent component, int interval) { this.colors = colors; this.component = component; component.addFocusListener( this ); timer = new Timer(interval, this); } public void focusGained(FocusEvent e) { alpha = 0; increment = 1; timer.start(); } public void focusLost(FocusEvent e) { alpha = steps; increment = -1; timer.start(); } public void actionPerformed(ActionEvent e) { alpha += increment; component.setBackground( (Color)colors.get(alpha) ); if (alpha == steps || alpha == 0) timer.stop(); } } public static void main(String[] args) { // Create test components JComponent textField1 = new JTextField(10); textField1.setBackground( Color.YELLOW ); JComponent textField3 = new JTextField(10); JComponent textField4 = new JTextField(10); JComponent button = new JButton("Start"); JComponent checkBox = new JCheckBox("Check Box"); JFrame frame = new JFrame("Fading Background"); frame.setDefaultCloseOperation( JFrame.EXIT_ON_CLOSE ); frame.getContentPane().add(textField1, BorderLayout.NORTH ); frame.getContentPane().add(button, BorderLayout.SOUTH ); frame.getContentPane().add(textField3, BorderLayout.WEST ); frame.getContentPane().add(textField4, BorderLayout.EAST ); frame.getContentPane().add(checkBox); // Gradual Fading (using defaults) // Fader fader = new Fader( new Color(155, 255, 155) ); Fader fader = new Fader( new Color(155, 255, 155), 10, 50 ); fader.add( textField1 ); fader.add( textField3 ); fader.add( checkBox ); // Instant Fading fader = new Fader( new Color(255, 155, 155), 1, 1 ); fader.add( textField4 ); fader.add( button ); frame.pack(); frame.setVisible( true ); } } 

它使用Timer以指定的间隔更新背景。 然后它根据所需的步数在两种颜色之间进行插值。

看看这个例子:

 import javax.swing.*; import java.awt.*; import java.awt.event.*; public class Test { public static void main(String args[]) { final JFrame frame = new JFrame(); frame.setBounds(100, 100, 300, 300); frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); // set some random initial color final Component comp = frame.getContentPane(); comp.setBackground(new Color( (float) Math.random(), (float) Math.random(), (float) Math.random())); frame.setVisible(true); final Timer timer = new Timer(50, new ActionListener() { @Override public void actionPerformed(ActionEvent evt) { final Color targetColor = new Color(30,40,50); final int changingSpeed = 5; final Color currentColor = comp.getBackground(); // step 1 int r = currentColor.getRed(); int g = currentColor.getGreen(); int b = currentColor.getBlue(); // step 2 double dr = targetColor.getRed() - r; double dg = targetColor.getGreen() - g; double db = targetColor.getBlue() - b; // step 3 double norm = Math.sqrt(dr*dr+dg*dg+db*db); if (norm < .001) { ((Timer)(evt.getSource())).stop(); return; } dr /= norm; dg /= norm; db /= norm; // step 4 dr *= Math.min(changingSpeed, norm); dg *= Math.min(changingSpeed, norm); db *= Math.min(changingSpeed, norm); // step 5 r += dr; g += dg; b += db; comp.setBackground(new Color(r,g,b)); frame.repaint(); } }); timer.start(); } } 

有几点需要注意:

  1. 使用计时器触发更新。 这确保了它们在EventThread中完成,这是操作Swing GUI所必需的。

  2. 测试非常小的标准。 这意味着,您的delta接近于零,您应该停止更新颜色。

  3. 使用最小的标准和你的变化速度。 如果您的变化速度足够高,您的颜色将在目标颜色周围交替,您的增量将永久地交换符号,并且过程不会终止。 因此,如果您的delta小于您的变化速度,请使用delta!

  4. 完成操作颜色后,不要忘记调用重绘。

是的,而不是使用for循环

而(I <= 10);

你可以实际使用你的变化速度。 同时你的改变速度应该

是一个长度为3的数组,因为颜色间隔的不同偏移即

dr,dg,db这样他们可以独立照顾。

喜欢这个…

 int [] changingSpeed(int []Offset){ int loop = 5; // 5 means the color should be change 5 times int [] changeSpeed = new int[3]; changeSpeed[0]= offset[0]/loop; changeSpeed[1]= offset[1]/loop; changeSpeed[2]= offset[2]/loop; return changeSpeed; } // your update method will now look like this updateColor(int [] changeSpeed) throws AWTException{ int dr = changeSpeed[0]; int dg = changeSpeed[1]; int db = changeSpeed[2]; Robot slow = new Robot(); int i=0; int f= loop; // the number of time you want the color to change while(i<=f){ slow.delay(1000) //sleep will sleep for 1000ms setColor(targetColor.getRed() + dr/10, targetColor.getGreen(),targetColor.getBlue()); setColor(targetColor.getRed(), targetColor.getGreen() + (dg/10),targetColor.getBlue()); setColor(targetColor.getRed(), targetColor.getGreen(),targetColor.getBlue() + db/10); i++; } } 

我不会依赖特定的延迟时间来计算动画中的下一步,因为这肯定会在不同的机器中提供不同的执行时间,尽管它可能不是相关的差异。

您可以使用长度代表动画总时间而不是更改速度因子,并使用Thread (或其他multithreading机制)来控制动画生命周期(例如计算自上次重绘以来已经过了多少时间,以及完成百分比是什么用于下一次迭代)。

我不知道我是否按照你的数学指示。

我创建了一个Swing GUI,这样我就可以看到从一种颜色到另一种颜色的过渡。

换色测试仪

“重置颜色”按钮生成随机起始颜色和随机整理颜色。 “开始”按钮将底部面板从起始颜色转换为精加工颜色。 每个变换在红色,绿色或蓝色维度上移动大约5个值,延迟为300毫秒。

我倾向于观察数字的变化,而不是颜色。 我想确保转型相当均匀。

无论如何,这是代码。 您可以使用它来建模其他Swing GUI。 我将所有类放在一个文件中,这样我就可以在这里粘贴代码了。 您应该将类​​分成单独的文件。

 package com.ggl.testing; import java.awt.BorderLayout; import java.awt.Color; import java.awt.Component; import java.awt.Container; import java.awt.Dimension; import java.awt.Graphics; import java.awt.GridBagConstraints; import java.awt.GridBagLayout; import java.awt.Insets; import java.awt.event.ActionEvent; import java.awt.event.ActionListener; import javax.swing.JButton; import javax.swing.JFrame; import javax.swing.JLabel; import javax.swing.JPanel; import javax.swing.SwingUtilities; public class ColorChangeTest implements Runnable { private static final Insets normalInsets = new Insets(10, 10, 0, 10); private Color currentColor; private Color targetColor; private ColorPanel changingColorPanel; private ColorPanel currentColorPanel; private ColorPanel targetColorPanel; private JLabel changingLabel; private JLabel currentLabel; private JLabel targetLabel; public static void main(String args[]) { SwingUtilities.invokeLater(new ColorChangeTest()); } @Override public void run() { JFrame frame = new JFrame("Color Change Test"); frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); JPanel mainPanel = new JPanel(); mainPanel.setLayout(new BorderLayout()); mainPanel.add(createColorPanel(), BorderLayout.NORTH); mainPanel.add(createChangingPanel(), BorderLayout.CENTER); frame.add(mainPanel); frame.pack(); frame.setLocationByPlatform(true); frame.setVisible(true); } private JPanel createColorPanel() { JPanel colorPanel = new JPanel(); setNewColors(); JPanel currentPanel = new JPanel(); currentPanel.setLayout(new BorderLayout()); JPanel currentLabelPanel = new JPanel(); currentLabelPanel.setLayout(new BorderLayout()); JLabel startLabel = new JLabel("Starting Color"); startLabel.setHorizontalAlignment(JLabel.CENTER); currentLabelPanel.add(startLabel, BorderLayout.NORTH); currentLabel = new JLabel(getColorString(currentColor)); currentLabel.setHorizontalAlignment(JLabel.CENTER); currentLabelPanel.add(currentLabel, BorderLayout.SOUTH); currentPanel.add(currentLabelPanel, BorderLayout.NORTH); currentColorPanel = new ColorPanel(100, 100, currentColor); currentPanel.add(currentColorPanel, BorderLayout.CENTER); colorPanel.add(currentPanel); JPanel targetPanel = new JPanel(); targetPanel.setLayout(new BorderLayout()); JPanel targetLabelPanel = new JPanel(); targetLabelPanel.setLayout(new BorderLayout()); JLabel finishLabel = new JLabel("Finishing Color"); finishLabel.setHorizontalAlignment(JLabel.CENTER); targetLabelPanel.add(finishLabel, BorderLayout.NORTH); targetLabel = new JLabel(getColorString(targetColor)); targetLabel.setHorizontalAlignment(JLabel.CENTER); targetLabelPanel.add(targetLabel, BorderLayout.SOUTH); targetPanel.add(targetLabelPanel, BorderLayout.NORTH); targetColorPanel = new ColorPanel(100, 100, targetColor); targetPanel.add(targetColorPanel, BorderLayout.CENTER); colorPanel.add(targetPanel); colorPanel.add(createButtonPanel()); return colorPanel; } private JPanel createButtonPanel() { JPanel buttonPanel = new JPanel(); buttonPanel.setLayout(new GridBagLayout()); int gridy = 0; JButton resetButton = new JButton("Reset Colors"); resetButton.addActionListener(new ResetColorsListener(this)); addComponent(buttonPanel, resetButton, 0, gridy++, 1, 1, normalInsets, GridBagConstraints.LINE_START, GridBagConstraints.HORIZONTAL); JButton startButton = new JButton("Start"); startButton.addActionListener(new ColorChangeListener(this)); addComponent(buttonPanel, startButton, 0, gridy++, 1, 1, normalInsets, GridBagConstraints.LINE_START, GridBagConstraints.HORIZONTAL); return buttonPanel; } private JPanel createChangingPanel() { JPanel changingPanel = new JPanel(); changingPanel.setLayout(new BorderLayout()); changingLabel = new JLabel(getColorString(currentColor)); changingLabel.setHorizontalAlignment(JLabel.CENTER); changingPanel.add(changingLabel, BorderLayout.NORTH); changingColorPanel = new ColorPanel(300, 200, currentColor); changingPanel.add(changingColorPanel, BorderLayout.CENTER); return changingPanel; } public void setChangingColorLabelText(Color color) { changingLabel.setText(getColorString(color)); } public void setNewColors() { currentColor = getRandomColor(); targetColor = getRandomColor(); } public void displayNewColors() { currentLabel.setText(getColorString(currentColor)); targetLabel.setText(getColorString(targetColor)); changingLabel.setText(getColorString(currentColor)); currentColorPanel.setColor(currentColor); targetColorPanel.setColor(targetColor); changingColorPanel.setColor(currentColor); } public Color getCurrentColor() { return currentColor; } public Color getTargetColor() { return targetColor; } public ColorPanel getChangingColorPanel() { return changingColorPanel; } private Color getRandomColor() { return new Color((float) Math.random(), (float) Math.random(), (float) Math.random()); } private String getColorString(Color color) { int r = color.getRed(); int g = color.getGreen(); int b = color.getBlue(); return "(" + r + ", " + g + ", " + b + ")"; } private void addComponent(Container container, Component component, int gridx, int gridy, int gridwidth, int gridheight, Insets insets, int anchor, int fill) { GridBagConstraints gbc = new GridBagConstraints(gridx, gridy, gridwidth, gridheight, 1.0D, 1.0D, anchor, fill, insets, 0, 0); container.add(component, gbc); } public class ColorPanel extends JPanel { private static final long serialVersionUID = -2894328511698328096L; private Color color; public ColorPanel(int width, int height, Color color) { this.color = color; this.setPreferredSize(new Dimension(width, height)); } public void setColor(Color color) { this.color = color; repaint(); } @Override protected void paintComponent(Graphics g) { super.paintComponent(g); g.setColor(color); g.fillRect(0, 0, getWidth(), getHeight()); } } public class ResetColorsListener implements ActionListener { private ColorChangeTest colorChangeTest; public ResetColorsListener(ColorChangeTest colorChangeTest) { this.colorChangeTest = colorChangeTest; } @Override public void actionPerformed(ActionEvent event) { colorChangeTest.setNewColors(); colorChangeTest.displayNewColors(); } } public class ColorChangeListener implements ActionListener { private ColorChangeTest colorChangeTest; public ColorChangeListener(ColorChangeTest colorChangeTest) { this.colorChangeTest = colorChangeTest; } @Override public void actionPerformed(ActionEvent event) { ColorChange colorChange = new ColorChange(colorChangeTest); new Thread(colorChange).start(); } } public class ColorChange implements Runnable { private static final long sleepTime = 300L; private double r, g, b, dr, dg, db; private int tr, tg, tb, cr, cg, cb; private ColorChangeTest colorChangeTest; public ColorChange(ColorChangeTest colorChangeTest) { this.colorChangeTest = colorChangeTest; } @Override public void run() { calculateColorChange(); sleep(sleepTime); while (calculateNextColor()) { sleep(sleepTime); } setColor(colorChangeTest.getTargetColor()); } private void calculateColorChange() { double increment = 5D; // step 1 r = cr = colorChangeTest.getCurrentColor().getRed(); g = cg = colorChangeTest.getCurrentColor().getGreen(); b = cb = colorChangeTest.getCurrentColor().getBlue(); // step 2 tr = colorChangeTest.getTargetColor().getRed(); tg = colorChangeTest.getTargetColor().getGreen(); tb = colorChangeTest.getTargetColor().getBlue(); dr = tr - cr; dg = tg - cg; db = tb - cb; // step 3 double d = Math.sqrt(dr * dr + dg * dg + db * db); int steps = (int) (d / increment); dr /= (double) steps; dg /= (double) steps; db /= (double) steps; setColor(new Color(cr, cg, cb)); } private boolean calculateNextColor() { // step 5 r += dr; g += dg; b += db; if (isFinished()) { return false; } else { setColor(new Color(round(r), round(g), round(b))); return true; } } private boolean isFinished() { return isColorFinished(cr, tr, round(r)) || isColorFinished(cg, tg, round(g)) || isColorFinished(cb, tb, round(b)); } private int round(double value) { return (int) Math.round(value); } private boolean isColorFinished(int original, int target, int current) { boolean isFinished = false; if (current < 0 || current > 255) { isFinished = true; } else if ((target >= original) && (current >= target)) { isFinished = true; } else if ((target <= original) && (current <= target)) { isFinished = true; } return isFinished; } private void setColor(final Color color) { SwingUtilities.invokeLater(new Runnable() { @Override public void run() { colorChangeTest.getChangingColorPanel().setColor(color); colorChangeTest.setChangingColorLabelText(color); } }); } private void sleep(long sleepTime) { try { Thread.sleep(sleepTime); } catch (InterruptedException e) { } } } } 

为了实现缓慢的行为,使用Robot类的实例可能是个好主意。

 //Method to obtain the offset of the color static int [] getColorOffset(Color initial, Color final){ int [] colorOffset = new int[3]; colorOffset[0]= final.getRed()-initial.getRed(); colorOffset[1] = final.getGreen()-initial.getGreen(); colorOffset[2]= final.getBlue()-initial.getBlue(); return colorOffset; } updateColor(int [] colorOffset) throws AWTException{ int dr = colorOffset[0]; int dg = colorOffset[1]; int db = colorOffset[2]; Robot slow = new Robot(); int i=0; while(i<=10){ slow.delay(1000) //sleep will sleep for 1000ms setColor(targetColor.getRed() + dr/10, targetColor.getGreen(),targetColor.getBlue()); setColor(targetColor.getRed(), targetColor.getGreen() + (dg/10),targetColor.getBlue()); setColor(targetColor.getRed(), targetColor.getGreen(),targetColor.getBlue() + db/10); i++; } } public static void main(String args[]) throws AWTException{ Color initial = Color.black; Color final = Color,white; int [] colorOffset = getColorOffset(initial, final); updateColor(colorOffset); }