【问题标题】:Color fading algorithm?褪色算法?
【发布时间】:2012-10-24 18:07:19
【问题描述】:

我正在创建一些自定义 Swing 组件,我希望这些组件从一种颜色渐变为另一种颜色。目前我正在从 RGB 转换为 HSB,然后通过 Hue 值递增并在绘画之前转换回 RGB,工作正常。

但是,这会循环显示所有颜色(即尝试从蓝色渐变到绿色循环通过黄色、橙色、红色等)。有没有一种像样的算法/方法可以直接从一种颜色淡入另一种颜色?

编辑:我已经通过 Swing Timer 对其进行了更新(我尽量避免使用像瘟疫这样的线程接触组件)。今晚我会根据你的建议去尝试一下,谢谢大家!

【问题讨论】:

  • 只需增加 de RGB 值即可。
  • 对于example
  • 可以使用Trident提供的颜色插值。这是example

标签: java swing user-interface colors


【解决方案1】:

基于此example,下面的Queue<Color>N = 32 步骤中从Color.green 循环到Color.blue 并再次回到Color.green。请注意,在 HSB 模型中,Color.green 在数值上小于 Color.blue。另请参阅使用 HSB 的相关example

public Flash(JComponent component) {
    this.component = component;
    float gHue = Color.RGBtoHSB(0, 1, 0, null)[0];
    float bHue = Color.RGBtoHSB(0, 0, 1, null)[0];
    for (int i = 0; i < N; i++) {
        clut.add(Color.getHSBColor(gHue + (i * (bHue - gHue) / N), 1, 1));
    }
    for (int i = 0; i < N; i++) {
        clut.add(Color.getHSBColor(bHue - (i * (bHue - gHue) / N), 1, 1));
    }
}

【讨论】:

    【解决方案2】:

    我使用多种方法来实现相同的结果。

    Bascally 我使用一个模拟 API 接口作为 LinearGradientPaint,在其中我提供一个分数数组和一个颜色数组,然后基于一个 float 百分比,我计算得到的混合颜色。

    这使我可以通过相同的算法产生许多有效的结果。

    虽然此示例旨在演示一系列颜色的混合,但您可以简单地提供两种颜色和{0f, 1f} 的一部分用于两种颜色

    这也让我能够有效地制作彩色动画。

    public class ColorFade {
    
        public static void main(String[] args) {
            new ColorFade();
        }
    
        public ColorFade() {
            EventQueue.invokeLater(new Runnable() {
                @Override
                public void run() {
                    try {
                        UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName());
                    } catch (ClassNotFoundException ex) {
                    } catch (InstantiationException ex) {
                    } catch (IllegalAccessException ex) {
                    } catch (UnsupportedLookAndFeelException ex) {
                    }
    
                    JFrame frame = new JFrame();
                    frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
                    frame.setLayout(new BorderLayout());
    //                frame.add(new FadePane());
                    frame.add(new ColorFadePane());
                    frame.pack();
                    frame.setLocationRelativeTo(null);
                    frame.setVisible(true);
                }
            });
        }
    
        public class FadePane extends JPanel {
    
            private float[] fractions = new float[]{0f, 0.25f, 0.5f, 1f};
            private Color[] colors = new Color[]{Color.GREEN, Color.BLUE, Color.YELLOW, Color.RED};
            private float direction = 0.05f;
            private float progress = 0f;
    
            public FadePane() {
                Timer timer = new Timer(125, new ActionListener() {
                    @Override
                    public void actionPerformed(ActionEvent e) {
                        if (progress + direction > 1f) {
                            direction = -0.05f;
                        } else if (progress + direction < 0f) {
                            direction = 0.05f;
                        }
                        progress += direction;
                        repaint();
                    }
                });
                timer.setCoalesce(true);
                timer.setRepeats(true);
                timer.start();
            }
    
            @Override
            public Dimension getPreferredSize() {
                return new Dimension(100, 100);
            }
    
            @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 class ColorFadePane extends JPanel {
    
            private float[] fractions = new float[]{0f, 0.25f, 0.5f, 1f};
            private Color[] colors = new Color[]{Color.GREEN, Color.BLUE, Color.YELLOW, Color.RED};
    
            public ColorFadePane() {
            }
    
            @Override
            public Dimension getPreferredSize() {
                return new Dimension(200, 100);
            }
    
            @Override
            protected void paintComponent(Graphics g) {
                super.paintComponent(g);
    
                Graphics2D g2d = (Graphics2D) g.create();
                int width = getWidth();
                int height = getHeight();
                int bandWidth = width / 100;
                for (int index = 0; index < 100; index++) {
                    float progress = (float)index / (float)100;
                    Color color = blendColors(fractions, colors, progress);
    
                    int x = bandWidth * index;
                    int y = 0;
                    g2d.setColor(color);
                    g2d.fillRect(x, y, bandWidth, 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;
        }
    }
    

    【讨论】:

      【解决方案3】:

      最简单的方法是在每个 RGB 值之间进行插值。 这对所有语言都是一样的——python代码看起来像:

      steps = 10
      
      rgb1 = [ 'AA', '08', 'C3' ]
      rgb2 = [ '03', '88', '1C' ]
      
      h1 = map( lambda s: int( '0x'+s, 0 ), rgb1 )
      h2 = map( lambda s: int( '0x'+s, 0 ), rgb2 )
      
      inc = [0, 0, 0]
      for i in range(0,3):
          inc[i] = ( h2[i] - h1[i] ) / ( steps - 1 )
      
      for i in range(0,steps-1):
          print '<span style="background: #%02x%02x%02x"> &nbsp; %i &nbsp; </span>' % ( 
                  h1[0] + i * inc[0],
                  h1[1] + i * inc[1],
                  h1[2] + i * inc[2],
                  i+1 )
      
      print '<span style="background: #%02x%02x%02x"> &nbsp; %i &nbsp; </span>' % ( 
              h2[0], h2[1], h2[2], steps )
      

      【讨论】:

      【解决方案4】:

      提示

      1. 使用Swing Timer 获得慢动作效果
      2. 将 RGB 的值递减一个数字,例如 x

      Tadaaaa.. :-)

      更新:如果您愿意,您可以使用 x 的值。

      在执行过程中使用Math.Random()函数生成伪随机值到x

      HovercraftFullOfEels &mKorbel,感谢您的意见

      【讨论】:

      • Swing Timer 比线程工作得更好、更容易。
      • @HovercraftFullOfEels :关于摆动的一点想法。我的回答完全基于我对 java 的了解。但我确信Swings 会有所收获。
      • 如果您不小心使用了 Thread.sleep,您可能会冻结 Swing 事件线程,从而冻结整个应用程序。如果您直接使用后台线程,那么您必须注意确保所有 Swing 调用都使用 SwingUtilities.invokeLater(new Runnable() {...}); 在 Swing 事件线程上正确排队。由于这是一个与 Swing 相关的问题,因此最好的解决方案是使用 Swing 已经为这类事情提供的工具,即 Swing Timer。
      • @HovercraftFullOfEels :我完全同意你的看法。如果它是一个摇摆应用程序,摇摆为它提供了一些东西。使用它是明智的。如果不“小心处理”,是的,线程可能会发疯,哈哈
      • @Mukul Goel wrote use threads.sleep to give a slow motion effect.,不是从来没有,有一堆类似的问题,其中 Thread.sleep(int) 不起作用,对于 AWT 组件可能是方式,但对于 Swing JComponents 可能是非常矛盾,可能是一个很好的答案,但将其更改为 Swing Timer
      【解决方案5】:

      您可以线性插值从起始 rgb 颜色到您最终想要的颜色的过渡。

      这意味着如果例如您将rgb(255,255,0) 作为起始颜色,将rgb(50,50,50) 作为目标,并且您希望通过(-41 = (255-50)/5, -41, 10) 在每一步中调整为以下颜色的5 个步骤达到最终颜色:

      rgb(255,255,  0)
      rgb(214,214, 10)
      rgb(173,173, 20)
      rgb(132,132, 30)
      rgb( 91, 91, 40)
      rgb( 50, 50, 50)
      

      这称为线性渐变,很容易实现,但当然还有各种其他技术可以在颜色之间进行良好的过渡。

      【讨论】:

        猜你喜欢
        • 2017-08-10
        • 2021-09-03
        • 2012-06-02
        • 2015-03-16
        • 2012-05-29
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多