Search code examples
javaswingjframejpanelpaintcomponent

Opacity in Color is not being drawn on JPanel


I am using paintComponent to make a fade-in opening. Though I can use transparent images to create this effect, I feel like drawing is both space conservative and efficient, but when I had tried to make code for it which is provided below

Graphics2D painter = (Graphics2D)g;
        int paint = 0;
        
        
        if (paint!=255) {
            painter.setColor(new Color(0, 0, 0, paint));
            paint+=17;
            painter.drawImage(frm1,0,-16,768,576,null);
            painter.fillRect(0, 0, SCREEN_WIDTH, SCREEN_HEIGHT);
            
            try {
                Thread.sleep(200);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }

The window starts with a white screen, later showing frm1 (the image that I want the opacity to overlay)

In the Frame's code, I tried typing the constructor (which contains the start to the game loop) after the frame.setVisible(true); line of code, this affected the code in no way whatsoever. Even though I can use transparent images, I am trying to make the game more lightweight, therefore I would prefer paintComponent.

The code for the panel is provided below

package studios.masterpiece.pts.display;

//AWT
import java.awt.Color;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.image.BufferedImage;
//IO
import java.io.IOException;

//ImageIO
import javax.imageio.ImageIO;
//Swing
import javax.swing.JPanel;
import javax.swing.Timer;

public class GameScene extends JPanel implements Runnable{
    
    private static final long serialVersionUID = -1892599432299801189L;
    
    Timer timer;
    
    Graphics painter;
    BufferedImage frm1;
    
    Thread repeat;
    
    //References
    static int tileSize = 48;
    
    static int rows = 16;
    static int columns = 12;
    
    public static int SCREEN_WIDTH = tileSize*rows; //768  
    public static int SCREEN_HEIGHT = tileSize*columns;
    
    //GameScene Properties
    public GameScene() {
        this.setSize(SCREEN_WIDTH,SCREEN_HEIGHT);
        this.setBackground(Color.BLACK);
        
        this.setFocusable(true);
        this.setDoubleBuffered(true);
        
        startRepeat();
        
        //Getting the image I want the opacity to overlay
        try {
            frm1 = ImageIO.read(getClass().getResourceAsStream("/studios/masterpiece/pts/animations/intro/Intro1.png"));
        } 
        catch (IOException e) {e.printStackTrace();}
    }
    
    public void startRepeat() {
        repeat = new Thread(this);
        repeat.start();
        
        //CURRENTLY DOES NOT WORK
        timer = new Timer(250, new ActionListener() {
            
            @Override
            public void actionPerformed(ActionEvent event) {
                Timer timer = (Timer) event.getSource();
                int opacity = this.getOpacity();
                opacity += 15;
                if (opacity < 255) {
                    this.setOpacity(opacity);
                } else {
                    opacity = 255;
                    this.setOpacity(opacity);
                    timer.stop();
                }
                this.repaint();
            }
        });
        timer.start();
        //UNTIL HERE
    }
    public void paintComponent(Graphics g) {
        
        super.paintComponent(g);
        
        Graphics2D painter = (Graphics2D)g;
        
        this.painter=painter;       
    }

    @Override
    public void run() {
        while (repeat!=null) {
            repaint();
            
        }
    }

}

Solution

  • Here's an example of a fade-in GUI.

    Here's the initial GUI:

    Initial GUI

    Here's the GUI after the image is faded in:

    Final GUI

    So, How did I do this?

    I created a JFrame and a drawing JPanel. I drew the background and the text using Color instances with a specified opacity or alpha component.

    I used a Swing Timer to adjust the opacity every 250 milliseconds and repaint the drawing JPanel.

    Here's the complete runnable code. I made the additional class an inner class so I could post the code as one block.

    import java.awt.BorderLayout;
    import java.awt.Color;
    import java.awt.Dimension;
    import java.awt.Font;
    import java.awt.FontMetrics;
    import java.awt.Graphics;
    import java.awt.Rectangle;
    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;
    
    public class FadeInGUI implements Runnable {
    
        public static void main(String[] args) {
            SwingUtilities.invokeLater(new FadeInGUI());
        }
    
        private final DrawingPanel drawingPanel;
    
        public FadeInGUI() {
            this.drawingPanel = new DrawingPanel();
        }
    
        @Override
        public void run() {
            JFrame frame = new JFrame("Fade In GUI");
            frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
    
            frame.add(drawingPanel, BorderLayout.CENTER);
    
            frame.pack();
            frame.setLocationByPlatform(true);
            frame.setVisible(true);
    
            Timer timer = new Timer(250, new ActionListener() {
                @Override
                public void actionPerformed(ActionEvent event) {
                    Timer timer = (Timer) event.getSource();
                    int opacity = drawingPanel.getOpacity();
                    opacity += 15;
                    if (opacity < 255) {
                        drawingPanel.setOpacity(opacity);
                    } else {
                        opacity = 255;
                        drawingPanel.setOpacity(opacity);
                        timer.stop();
                    }
                    drawingPanel.repaint();
                }
            });
            timer.start();
        }
    
        public class DrawingPanel extends JPanel {
    
            private static final long serialVersionUID = 1L;
    
            private int opacity;
    
            public DrawingPanel() {
                this.opacity = 0;
                this.setPreferredSize(new Dimension(640, 480));
            }
    
            public void setOpacity(int opacity) {
                this.opacity = opacity;
            }
    
            public int getOpacity() {
                return opacity;
            }
    
            @Override
            protected void paintComponent(Graphics g) {
                super.paintComponent(g);
    
                Color backgroundColor = new Color(255, 255, 0, opacity);
                Color textColor = new Color(0, 0, 255, opacity);
    
                g.setColor(backgroundColor);
                g.fillRect(0, 0, getWidth(), getHeight());
    
                String text = "Amazing Game";
                Font titleFont = getFont().deriveFont(Font.BOLD, 48f);
                Rectangle r = new Rectangle(0, 0, getWidth(), getHeight() / 3);
                drawCenteredString(g, text, r, textColor, titleFont);
    
                text = "by Amazing Company";
                Font subtitleFont = getFont().deriveFont(Font.BOLD, 32f);
                r = new Rectangle(0, getHeight() * 2 / 3, getWidth(),
                        getHeight() / 3);
                drawCenteredString(g, text, r, textColor, subtitleFont);
            };
    
            /**
             * Draw a String centered in the middle of a Rectangle.
             *
             * @param g    The Graphics instance.
             * @param text The String to draw.
             * @param rect The Rectangle to center the text in.
             */
            private void drawCenteredString(Graphics g, String text, Rectangle rect,
                    Color color, Font font) {
                FontMetrics metrics = g.getFontMetrics(font);
                int x = rect.x + (rect.width - metrics.stringWidth(text)) / 2;
                int y = rect.y + ((rect.height - metrics.getHeight()) / 2)
                        + metrics.getAscent();
                g.setFont(font);
                g.setColor(color);
                g.drawString(text, x, y);
            }
    
        }
    
    }