Java 如何缓慢地将对象颜色从一种更改为另一种?

Java 如何缓慢地将对象颜色从一种更改为另一种?,java,swing,colors,Java,Swing,Colors,我正在尝试实现一种场景,即对象的颜色从一种颜色缓慢更改为另一种颜色 我的初始颜色为targetColor,最终颜色为updateColor。changingSpeed变量设置为5 我必须使用的机制是 使用getRed(),getGreen(),getBlue()获得红色、绿色和蓝色 通过TargetColor–color=[dr dg db]计算目标颜色的差异 通过将向量[dr dg db]T的范数除以零(注意div) 将其乘以更改速度,以控制更改颜色的速度 将颜色更新为颜色+[dr'dg'db

我正在尝试实现一种场景,即对象的颜色从一种颜色缓慢更改为另一种颜色

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

我必须使用的机制是

  • 使用
    getRed()
    getGreen()
    getBlue()
    获得红色、绿色和蓝色
  • 通过TargetColor–color=[dr dg db]计算目标颜色的差异
  • 通过将向量[dr dg db]T的范数除以零(注意div)
  • 将其乘以更改速度,以控制更改颜色的速度
  • 将颜色更新为颜色+[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步? 请任何人通过代码示例指定如何执行此操作?
    另外,请检查上述代码是否正确。

    为了实现缓慢行为,最好使用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);
    }
    
    //获取颜色偏移的方法
    静态int[]getColorOffset(颜色初始值、颜色最终值){
    int[]colorOffset=新int[3];
    colorOffset[0]=final.getRed()-initial.getRed();
    colorOffset[1]=final.getGreen()-initial.getGreen();
    colorOffset[2]=final.getBlue()-initial.getBlue();
    返回色偏移量;
    }
    updateColor(int[]colorOffset)引发AWTException{
    int dr=颜色偏移量[0];
    int dg=色偏移量[1];
    int db=颜色偏移量[2];
    机器人慢=新机器人();
    int i=0;
    (i看这个例子:

    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();
        }
    }
    
    import javax.swing.*;
    导入java.awt.*;
    导入java.awt.event.*;
    公开课考试{
    公共静态void main(字符串参数[]){
    最终JFrame=新JFrame();
    机架立根(100100300300);
    frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
    //设置一些随机的初始颜色
    最终组件comp=frame.getContentPane();
    公司背景(新颜色)(
    (float)Math.random(),
    (float)Math.random(),
    (float)Math.random());
    frame.setVisible(true);
    最终计时器=新计时器(50,新ActionListener(){
    @凌驾
    已执行的公共无效操作(操作事件evt){
    最终颜色targetColor=新颜色(30,40,50);
    最终的整数变化速度=5;
    最终颜色currentColor=comp.getBackground();
    //第一步
    int r=currentColor.getRed();
    int g=currentColor.getGreen();
    int b=currentColor.getBlue();
    //步骤2
    double dr=targetColor.getRed()-r;
    double dg=targetColor.getGreen()-g;
    double db=targetColor.getBlue()-b;
    //步骤3
    双范数=数学sqrt(dr*dr+dg*dg+db*db);
    如果(标准<.001){
    ((计时器)(evt.getSource()).stop();
    返回;
    }
    dr/=常模;
    dg/=正常值;
    db/=标准值;
    //步骤4
    dr*=Math.min(变化速度,常模);
    dg*=数学最小值(变化速度,标准);
    db*=数学最小值(变化速度,标准);
    //步骤5
    r+=dr;
    g+=dg;
    b+=db;
    公司背景(新颜色(r、g、b));
    frame.repaint();
    }
    });
    timer.start();
    }
    }
    
    需要注意的几点:

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

  • 测试正常值是否非常小。这意味着,你的增量接近零,你应该停止更新颜色

  • 使用标准值和变化速度中的最小值。如果变化速度足够快,则颜色将围绕目标颜色交替,增量将永久交换符号,且过程不会终止。因此,如果增量小于变化速度,请使用增量

  • 操作完颜色后,别忘了调用repaint


  • 是的,而不是使用for循环


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


    您可以使用表示动画总时间的long来代替改变速度因子,并使用
    线程
    (或另一种多线程机制)来控制动画生命周期(例如计算自上次重新绘制以来经过的时间以及下一次迭代的完成百分比).

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

    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 );
        }
    }
    
    import java.awt.*;
    导入java.awt.event.*;
    导入java.util.Hashtable;
    导入java.util.ArrayList;
    导入javax.swing.*;
    公共级音量控制器
    {
    //组件具有焦点时的背景色
    私人色彩;
    //从原始背景淡入淡出背景的步骤
    私有int步骤;
    //在此时间间隔应用过渡色
    私有整数间隔;
    //存储从原始背景到淡入淡出背景的过渡色
    私有Hashtable backgroundColors=新Hashtable();
    /*
    *使用从背景色淡入指定颜色
    *默认值为以50毫秒的间隔执行10个步骤。
    *
    *@param fadeColor临时背景色
    */
    公共音量控制器(彩色音量控制器颜色)
    {
    这(fadeColor,10,50);
    }
    /*
    *从背景色淡入到背景中的指定颜色
    *在指定位置的指定步数
    
    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 );
        }
    }
    
    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) {
                }
            }
    
        }
    }