How can I make a Java Swing animation smoother

前端 未结 1 799
予麋鹿
予麋鹿 2020-11-29 09:39

I\'m creating an animated slide transition in Java, and it is choppy on my current model MacBook Pro and on my year-old iMac, on Java 6, 7, and 8.

What can I do to m

相关标签:
1条回答
  • 2020-11-29 10:36

    A lot depends on what it is you ultimately want to achieve.

    Remember, animation is the illusion of movement...

    I changed

    • the framesPerSecond to 60 which seems to have helped.
    • Reduced the overall height of the printable area
    • Calculated the area of change and simple called repaint(Rectangle) passing in only that area which has changed.

    The other problem is your animation has a large area to cover in a very short period of time. Increasing the amount of time will also make it smoother (or reducing the width and/or height)

    import java.awt.Color;
    import java.awt.Dimension;
    import java.awt.EventQueue;
    import java.awt.Graphics;
    import java.awt.Graphics2D;
    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.Timer;
    import javax.swing.UIManager;
    import javax.swing.UnsupportedLookAndFeelException;
    
    public class ScratchSpace {
    
        public static void main(String[] args) {
            EventQueue.invokeLater(new Runnable() {
                @Override
                public void run() {
                    try {
                        UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName());
                    } catch (ClassNotFoundException | InstantiationException | IllegalAccessException | UnsupportedLookAndFeelException ex) {
                    }
    
                    AnimatedPanel panel = new AnimatedPanel();
    
                    JFrame frame = new JFrame();
                    frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
                    frame.setContentPane(panel);
                    frame.pack();
                    frame.setLocationRelativeTo(null);
                    frame.setVisible(true);
    
                    panel.animate();
                }
            });
        }
    
        public static class AnimatedPanel extends JPanel {
    
            private float progress = 0.0f; // a number between 0.0 and 1.0
    
            public AnimatedPanel() {
                setPreferredSize(new Dimension(800, 100));
            }
    
            public void animate() {
                final int animationTime = 1000;
                int framesPerSecond = 60;
                int delay = 1000 / framesPerSecond;
                final long start = System.currentTimeMillis();
                final Timer timer = new Timer(delay, null);
                timer.addActionListener(new ActionListener() {
                    @Override
                    public void actionPerformed(ActionEvent e) {
                        final long now = System.currentTimeMillis();
                        final long elapsed = now - start;
    
                        int width = getWidth();
                        int height = getHeight();
                        int oldWidth = (int) (width * progress);
    
                        progress = (float) elapsed / animationTime;
                        int newWidth = (int) (width * progress);
    
                        repaint(new Rectangle(oldWidth, 0, newWidth - oldWidth, height));
                        if (elapsed >= animationTime) {
                            timer.stop();
                        }
                    }
                });
                timer.start();
            }
    
            @Override
            protected void paintComponent(Graphics g) {
                Graphics2D g2d = (Graphics2D) g;
                int width = getWidth();
                int progressWidth = (int) (width * progress);
                g2d.setColor(Color.BLUE);
                g2d.fillRect(0, 0, progressWidth, getHeight());
                g2d.setColor(Color.RED);
                g2d.fillRect(progressWidth, 0, width - progressWidth, getHeight());
            }
        }
    }
    

    Alternativly, you could generate you own backing buffer and update it...

    import java.awt.Color;
    import java.awt.Dimension;
    import java.awt.EventQueue;
    import java.awt.Graphics;
    import java.awt.Graphics2D;
    import java.awt.RenderingHints;
    import java.awt.event.ActionEvent;
    import java.awt.event.ActionListener;
    import java.awt.geom.Rectangle2D;
    import java.awt.image.BufferedImage;
    import javax.swing.JFrame;
    import javax.swing.JPanel;
    import javax.swing.Timer;
    import javax.swing.UIManager;
    import javax.swing.UnsupportedLookAndFeelException;
    
    public class ScratchSpace {
    
        public static void main(String[] args) {
            EventQueue.invokeLater(new Runnable() {
                @Override
                public void run() {
                    try {
                        UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName());
                    } catch (ClassNotFoundException | InstantiationException | IllegalAccessException | UnsupportedLookAndFeelException ex) {
                    }
    
                    AnimatedPanel panel = new AnimatedPanel();
    
                    JFrame frame = new JFrame();
                    frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
                    frame.setContentPane(panel);
                    frame.pack();
                    frame.setLocationRelativeTo(null);
                    frame.setVisible(true);
    
                    panel.animate();
                }
            });
        }
    
        public static class AnimatedPanel extends JPanel {
    
            private float progress = 0.0f; // a number between 0.0 and 1.0
            private BufferedImage buffer;
    
            public AnimatedPanel() {
                setPreferredSize(new Dimension(800, 600));
    //            setOpaque(true);
            }
    
            public void animate() {
                final int animationTime = 1000;
                int framesPerSecond = 60;
                int delay = 1000 / framesPerSecond;
                final long start = System.currentTimeMillis();
                final Timer timer = new Timer(delay, null);
                timer.addActionListener(new ActionListener() {
                    @Override
                    public void actionPerformed(ActionEvent e) {
                        final long now = System.currentTimeMillis();
                        final long elapsed = now - start;
    
                        int width = getWidth();
    
                        progress = (float) elapsed / animationTime;
    
                        updateBuffer();
                        repaint();
    
                        if (elapsed >= animationTime) {
                            timer.stop();
                        }
    
                    }
                });
                timer.start();
            }
    
            @Override
            public void invalidate() {
                buffer = null;
                updateBuffer();
                super.invalidate();
            }
    
            protected void updateBuffer() {
    
                if (getWidth() > 0 && getHeight() > 0) {
    
                    if (buffer == null) {
    
                        buffer = new BufferedImage(getWidth(), getHeight(), BufferedImage.TYPE_INT_RGB);
    
                    }
    
                    Graphics2D g2d = buffer.createGraphics();
                    g2d.setRenderingHint(RenderingHints.KEY_ALPHA_INTERPOLATION, RenderingHints.VALUE_ALPHA_INTERPOLATION_QUALITY);
                    g2d.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);
                    g2d.setRenderingHint(RenderingHints.KEY_COLOR_RENDERING, RenderingHints.VALUE_COLOR_RENDER_QUALITY);
                    g2d.setRenderingHint(RenderingHints.KEY_DITHERING, RenderingHints.VALUE_DITHER_ENABLE);
                    g2d.setRenderingHint(RenderingHints.KEY_FRACTIONALMETRICS, RenderingHints.VALUE_FRACTIONALMETRICS_ON);
                    g2d.setRenderingHint(RenderingHints.KEY_INTERPOLATION, RenderingHints.VALUE_INTERPOLATION_BILINEAR);
                    g2d.setRenderingHint(RenderingHints.KEY_RENDERING, RenderingHints.VALUE_RENDER_QUALITY);
                    g2d.setRenderingHint(RenderingHints.KEY_STROKE_CONTROL, RenderingHints.VALUE_STROKE_PURE);
                    int width = getWidth();
                    int height = getHeight();
                    float progressWidth = width * progress;
                    g2d.setColor(Color.BLUE);
                    g2d.fill(new Rectangle2D.Float(0, 0, progressWidth, height));
                    g2d.setColor(Color.RED);
                    g2d.fill(new Rectangle2D.Float(progressWidth, 0, width - progressWidth, height));
                    g2d.dispose();
    
                }
    
            }
    
            @Override
            protected void paintComponent(Graphics g) {
                Graphics2D g2d = (Graphics2D) g;
                if (buffer != null) {
                    g2d.drawImage(buffer, 0, 0, this);
                }
    //            int width = getWidth();
    //            int progressWidth = (int) (width * progress);
    //            g2d.setColor(Color.BLUE);
    //            g2d.fillRect(0, 0, progressWidth, getHeight());
    //            g2d.setColor(Color.RED);
    //            g2d.fillRect(progressWidth, 0, width - progressWidth, getHeight());
            }
        }
    }
    

    The major problem you have is basically the area you are trying to paint is to large for the time you want to paint it.

    Another Alternatively

    You could create a BufferedImage that represents the progress bar and move as the progress updates. This update creates a compatible buffered image, which should make it faster to renderer

    import java.awt.Color;
    import java.awt.Dimension;
    import java.awt.EventQueue;
    import java.awt.Graphics;
    import java.awt.Graphics2D;
    import java.awt.GraphicsConfiguration;
    import java.awt.GraphicsEnvironment;
    import java.awt.RenderingHints;
    import java.awt.Transparency;
    import java.awt.event.ActionEvent;
    import java.awt.event.ActionListener;
    import java.awt.geom.Rectangle2D;
    import java.awt.image.BufferedImage;
    import javax.swing.JFrame;
    import javax.swing.JPanel;
    import javax.swing.Timer;
    import javax.swing.UIManager;
    import javax.swing.UnsupportedLookAndFeelException;
    
    public class ScratchSpace {
    
        public static void main(String[] args) {
            EventQueue.invokeLater(new Runnable() {
                @Override
                public void run() {
                    try {
                        UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName());
                    } catch (ClassNotFoundException | InstantiationException | IllegalAccessException | UnsupportedLookAndFeelException ex) {
                    }
    
                    AnimatedPanel panel = new AnimatedPanel();
    
                    JFrame frame = new JFrame();
                    frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
                    frame.setContentPane(panel);
                    frame.pack();
                    frame.setLocationRelativeTo(null);
                    frame.setVisible(true);
    
                    panel.animate();
                }
            });
        }
    
        public static class AnimatedPanel extends JPanel {
    
            private float progress = 0.0f; // a number between 0.0 and 1.0
            private BufferedImage buffer;
    
            public AnimatedPanel() {
                setPreferredSize(new Dimension(800, 600));
    //            setOpaque(true);
            }
    
            public void animate() {
                final int animationTime = 1000;
                int framesPerSecond = 60;
                int delay = 1000 / framesPerSecond;
                final long start = System.currentTimeMillis();
                final Timer timer = new Timer(delay, null);
                timer.addActionListener(new ActionListener() {
                    @Override
                    public void actionPerformed(ActionEvent e) {
                        final long now = System.currentTimeMillis();
                        final long elapsed = now - start;
    
                        progress = (float) elapsed / animationTime;
    
                        repaint();
    
                        if (elapsed >= animationTime) {
                            timer.stop();
                        }
    
                    }
                });
                timer.start();
            }
    
            @Override
            public void invalidate() {
                buffer = null;
                updateBuffer();
                super.invalidate();
            }
    
            protected void updateBuffer() {
    
                if (getWidth() > 0 && getHeight() > 0) {
    
                    if (buffer == null) {
    
                        GraphicsConfiguration config = GraphicsEnvironment.getLocalGraphicsEnvironment().getDefaultScreenDevice().getDefaultConfiguration();
                        buffer = config.createCompatibleImage(getWidth(), getHeight(), Transparency.TRANSLUCENT);
                        buffer.coerceData(true);
    
                        Graphics2D g2d = buffer.createGraphics();
                        g2d.setRenderingHint(RenderingHints.KEY_ALPHA_INTERPOLATION, RenderingHints.VALUE_ALPHA_INTERPOLATION_QUALITY);
                        g2d.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);
                        g2d.setRenderingHint(RenderingHints.KEY_COLOR_RENDERING, RenderingHints.VALUE_COLOR_RENDER_QUALITY);
                        g2d.setRenderingHint(RenderingHints.KEY_DITHERING, RenderingHints.VALUE_DITHER_ENABLE);
                        g2d.setRenderingHint(RenderingHints.KEY_FRACTIONALMETRICS, RenderingHints.VALUE_FRACTIONALMETRICS_ON);
                        g2d.setRenderingHint(RenderingHints.KEY_INTERPOLATION, RenderingHints.VALUE_INTERPOLATION_BILINEAR);
                        g2d.setRenderingHint(RenderingHints.KEY_RENDERING, RenderingHints.VALUE_RENDER_QUALITY);
                        g2d.setRenderingHint(RenderingHints.KEY_STROKE_CONTROL, RenderingHints.VALUE_STROKE_PURE);
                        int width = getWidth();
                        int height = getHeight();
                        g2d.setColor(Color.BLUE);
                        g2d.fill(new Rectangle2D.Float(0, 0, width, height));
                        g2d.dispose();
    
                    }
    
                }
    
            }
    
            @Override
            protected void paintComponent(Graphics g) {
                Graphics2D g2d = (Graphics2D) g;
                int width = getWidth();
                int progressWidth = (int) (width * progress);
                int x = (progressWidth - width);
                System.out.println(progressWidth + "; " + x);
    //            g2d.setColor(Color.BLUE);
    //            g2d.fillRect(0, 0, progressWidth, getHeight());
                g2d.setColor(Color.RED);
                g2d.fillRect(progressWidth, 0, width - progressWidth, getHeight());
                g2d.drawImage(buffer, x, 0, this);
            }
        }
    }
    
    0 讨论(0)
提交回复
热议问题