摇摆动画运行极慢

Posted

技术标签:

【中文标题】摇摆动画运行极慢【英文标题】:Swing animation running extremely slow 【发布时间】:2013-01-30 22:16:31 【问题描述】:

我当前使用 Java Swing 运行的动画存在问题。这是一个离散事件模拟,基于文本的模拟工作正常,我只是在将模拟连接到 GUI 输出时遇到问题。

在本例中,我将模拟 10 辆汽车。这些汽车由JPanels 表示,稍后我将详细说明。

考虑一下,事件 process_car_arrival。每次计划执行此事件时,我都会在我的 Model 类中将一个 Car 对象添加到一个名为 carsArrayList 中。 Car 类具有以下相关属性:

Point currentPos; // The current position, initialized in another method when knowing route.
double speed; // giving the speed any value still causes the same problem but I have 5 atm.
RouteType route; // for this example I only consider one simple route

此外它还有以下方法move()

switch (this.route) 
    case EAST:
        this.currentPos.x -= speed; 
        return this.currentPos;
.
.
.
//only above is relevant in this example

这一切都很好。所以理论上汽车会沿着一条从东到西的笔直的道路行驶,因为我只需为每辆要移动的汽车调用move() 方法。

返回到 process_car_arrival 事件。添加 Car 对象后,它会调用 View 类中的方法 addCarToEast()。这会在从东到西的道路起点处添加一个 JPanel。

现在转到View 类,我有一个**单独的**线程,它执行以下操作(run() 方法):

@Override
    public void run() 
        while (true) 
            try 
                Thread.sleep(30);
             catch (InterruptedException e) 
                e.printStackTrace();
            
            if (!cars.isEmpty()) 

                cars.get(i).setLocation(
                        new Point(getModel.getCars().get(i).move()));

                if (i == cars.size() - 1) 
                    i = 0;
                 else 
                    i++;
                
            
        
    

上面确实首先将汽车从东向西平稳移动。但是在有 3-4 辆车移动后,它最终变得非常缓慢,而当我有 10 辆汽车移动时,它最终移动得很少。

澄清一下,目前在 Model 类中有一个 ArrayListCar 对象,在 View 类中还有一个 ArrayListJPanel 对象代表汽车.我正在尝试将Car 对象与JPanels 匹配,但我显然做得很糟糕。

我怀疑我在做一些非常低效的事情,但我不知道是什么。我最初认为它可能正在访问ArrayList 这么多,我想这会让它变得非常慢。

任何关于我可以更改以使其顺利运行的指针?

【问题讨论】:

我首先担心的是您似乎正在从事件调度线程外部更新 UI 元素。这是危险的,也是极其不可取的。查看Concurrency in Swing 了解更多详情。我遇到的第二个问题是这还远远不够。你能提供一个简单的工作示例吗?不需要汽车的图像,一个简单的矩形表示就足够了。 除了 MadProgrammer 所说的之外,另一个危险信号是您正在创建单独的 JPanel。自从我做任何与图形相关的事情以来已经有一段时间了,但我认为你应该使用 2D 库而不是移动“n”个 JPanel。 @MadProgrammer 我正在处理你给我的链接,并试图看看我是否能抓住任何东西。如果我无法让它工作,稍后会发回一个简单的工作示例。 @Joe 您能否详细说明不移动 JPanel 是什么意思?你的意思是为每个动作创建一个新的矩形吗?那会不会很慢(如果这就是你的意思)? @Jatt 移动面板和直接绘图可能对您现在没有太大的影响。您可能会发现,尤其是随着时间的推移,渲染到某种后备缓冲区,然后将其交换到 UI 会导致更新更快,但我说先运行逻辑 - 恕我直言 【参考方案1】:

基于之前的answer,下面的示例模拟了一个由三辆出租车组成的车队在一个矩形网格上随机移动。 javax.swing.Timer 以 5 Hz 驱动动画。模型和视图在CabPanel 中紧密耦合,但动画可能会提供一些有用的见解。特别是,您可能会增加出租车数量或降低计时器延迟。

import java.awt.BorderLayout;
import java.awt.Color;
import java.awt.Dimension;
import java.awt.EventQueue;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.GridLayout;
import java.awt.Point;
import java.awt.RenderingHints;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.text.DecimalFormat;
import java.util.ArrayList;
import java.util.List;
import java.util.Random;
import javax.swing.JButton;
import javax.swing.JComboBox;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JPanel;
import javax.swing.Timer;

/**
 * @see https://***.com/a/14887457/230513
 * @see https://***.com/questions/5617027
 */

public class FleetPanel extends JPanel 

    private static final Random random = new Random();
    private final MapPanel map = new MapPanel();
    private final JPanel control = new JPanel();
    private final List<CabPanel> fleet = new ArrayList<CabPanel>();
    private final Timer timer = new Timer(200, null);

    public FleetPanel() 
        super(new BorderLayout());
        fleet.add(new CabPanel("Cab #1", Hue.Cyan));
        fleet.add(new CabPanel("Cab #2", Hue.Magenta));
        fleet.add(new CabPanel("Cab #3", Hue.Yellow));
        control.setLayout(new GridLayout(0, 1));
        for (CabPanel cp : fleet) 
            control.add(cp);
            timer.addActionListener(cp.listener);
        
        this.add(map, BorderLayout.CENTER);
        this.add(control, BorderLayout.SOUTH);
    

    public void start() 
        timer.start();
    

    private class CabPanel extends JPanel 

        private static final String format = "000000";
        private final DecimalFormat df = new DecimalFormat(format);
        private JLabel name = new JLabel("", JLabel.CENTER);
        private Point point = new Point();
        private JLabel position = new JLabel(toString(point), JLabel.CENTER);
        private int blocks;
        private JLabel odometer = new JLabel(df.format(0), JLabel.CENTER);
        private final JComboBox colorBox = new JComboBox();
        private final JButton reset = new JButton("Reset");
        private final ActionListener listener = new ActionListener() 

            @Override
            public void actionPerformed(ActionEvent e) 
                int ds = random.nextInt(3) - 1;
                if (random.nextBoolean()) 
                    point.x += ds;
                 else 
                    point.y += ds;
                
                blocks += Math.abs(ds);
                update();
            
        ;

        public CabPanel(String s, Hue hue) 
            super(new GridLayout(1, 0));
            name.setText(s);
            this.setBackground(hue.getColor());
            this.add(map, BorderLayout.CENTER);
            for (Hue h : Hue.values()) 
                colorBox.addItem(h);
            
            colorBox.setSelectedIndex(hue.ordinal());
            colorBox.addActionListener(new ActionListener() 

                @Override
                public void actionPerformed(ActionEvent e) 
                    Hue h = (Hue) colorBox.getSelectedItem();
                    CabPanel.this.setBackground(h.getColor());
                    update();
                
            );
            reset.addActionListener(new ActionListener() 

                @Override
                public void actionPerformed(ActionEvent e) 
                    point.setLocation(0, 0);
                    blocks = 0;
                    update();
                
            );
            this.add(name);
            this.add(odometer);
            this.add(position);
            this.add(colorBox);
            this.add(reset);
        

        private void update() 
            position.setText(CabPanel.this.toString(point));
            odometer.setText(df.format(blocks));
            map.repaint();
        

        private String toString(Point p) 
            StringBuilder sb = new StringBuilder();
            sb.append(Math.abs(p.x));
            sb.append(p.x < 0 ? " W" : " E");
            sb.append(", ");
            sb.append(Math.abs(p.y));
            sb.append(p.y < 0 ? " N" : " S");
            return sb.toString();
        
    

    private class MapPanel extends JPanel 

        private static final int SIZE = 16;

        public MapPanel() 
            this.setPreferredSize(new Dimension(32 * SIZE, 32 * SIZE));
            this.setBackground(Color.lightGray);
        

        @Override
        protected void paintComponent(Graphics g) 
            super.paintComponent(g);
            Graphics2D g2d = (Graphics2D) g;
            g2d.setRenderingHint(
                RenderingHints.KEY_ANTIALIASING,
                RenderingHints.VALUE_ANTIALIAS_ON);
            int w = this.getWidth();
            int h = this.getHeight();
            g2d.setColor(Color.gray);
            for (int col = SIZE; col <= w; col += SIZE) 
                g2d.drawLine(col, 0, col, h);
            
            for (int row = SIZE; row <= h; row += SIZE) 
                g2d.drawLine(0, row, w, row);
            

            for (CabPanel cp : fleet) 
                Point p = cp.point;
                int x = SIZE * (p.x + w / 2 / SIZE) - SIZE / 2;
                int y = SIZE * (p.y + h / 2 / SIZE) - SIZE / 2;
                g2d.setColor(cp.getBackground());
                g2d.fillOval(x, y, SIZE, SIZE);
            
        
    

    public enum Hue 

        Cyan(Color.cyan), Magenta(Color.magenta), Yellow(Color.yellow),
        Red(Color.red), Green(Color.green), Blue(Color.blue),
        Orange(Color.orange), Pink(Color.pink);
        private final Color color;

        private Hue(Color color) 
            this.color = color;
        

        public Color getColor() 
            return color;
        
    

    private static void display() 
        JFrame f = new JFrame("Dispatch");
        f.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
        FleetPanel fp = new FleetPanel();
        f.add(fp);
        f.pack();
        f.setLocationRelativeTo(null);
        f.setVisible(true);
        fp.start();
    

    public static void main(String[] args) 
        EventQueue.invokeLater(new Runnable() 

            @Override
            public void run() 
                display();
            
        );
    

【讨论】:

谢谢先生 :) 伟大的洞察力。在 1 小时内阅读您的代码所学到的东西比我在沮丧中度过一整夜所学到的要多得多!【参考方案2】:

我忍不住……

我在屏幕上运行了 500 辆汽车,几乎没有减速(这不是最快的……大约 200-300 辆还不错……

这使用面板来表示每辆车。如果您想获得更好的性能,您可能需要考虑使用某种后备缓冲区。

public class TestAnimation10 

    public static void main(String[] args) 
        new TestAnimation10();
    

    public TestAnimation10() 
        EventQueue.invokeLater(new Runnable() 
            @Override
            public void run() 
                try 
                    UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName());
                 catch (Exception ex) 
                

                final TrackPane trackPane = new TrackPane();
                JSlider slider = new JSlider(1, 500);
                slider.addChangeListener(new ChangeListener() 
                    @Override
                    public void stateChanged(ChangeEvent e) 
                        trackPane.setCongestion(((JSlider)e.getSource()).getValue());
                    
                );
                slider.setValue(5);

                JFrame frame = new JFrame("Test");
                frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
                frame.setLayout(new BorderLayout());
                frame.add(trackPane);
                frame.add(slider, BorderLayout.SOUTH);
                frame.pack();
                frame.setLocationRelativeTo(null);
                frame.setVisible(true);

            
        );
    

    public class TrackPane extends JPanel 

        private List<Car> cars;
        private int maxCars = 1;

        private List<Point2D[]> points;

        private Ellipse2D areaOfEffect;

        public TrackPane() 

            points = new ArrayList<>(25);

            cars = new ArrayList<>(25);
            setLayout(null);

            Timer timer = new Timer(40, new ActionListener() 
                @Override
                public void actionPerformed(ActionEvent e) 

                    Rectangle bounds = areaOfEffect.getBounds();
                    List<Car> tmp = new ArrayList<>(cars);
                    for (Car car : tmp) 
                        car.move();
                        if (!bounds.intersects(car.getBounds())) 
                            remove(car);
                            cars.remove(car);
                        
                    
                    updatePool();
                    repaint();
                
            );

            timer.setRepeats(true);
            timer.setCoalesce(true);
            timer.start();

            updateAreaOfEffect();
        

        protected void updateAreaOfEffect() 
            double radius = Math.max(getWidth(), getHeight()) * 1.5d;
            double x = (getWidth() - radius) / 2d;
            double y = (getHeight() - radius) / 2d;
            areaOfEffect = new Ellipse2D.Double(x, y, radius, radius);
        

        @Override
        public void invalidate() 
            super.invalidate();
            updateAreaOfEffect();
        

        protected void updatePool() 
            while (cars.size() < maxCars) 
//            if (cars.size() < maxCars) 
                Car car = new Car();
                double direction = car.getDirection();
                double startAngle = direction - 180;

                double radius = areaOfEffect.getWidth();
                Point2D startPoint = getPointAt(radius, startAngle);

                int cx = getWidth() / 2;
                int cy = getHeight() / 2;

                double x = cx + (startPoint.getX() - car.getWidth() / 2);
                double y = cy + (startPoint.getY() - car.getHeight() / 2);
                car.setLocation((int)x, (int)y);

                Point2D targetPoint = getPointAt(radius, direction);

                points.add(new Point2D[]startPoint, targetPoint);

                add(car);

                cars.add(car);
            
        

        @Override
        public void paint(Graphics g) 
            super.paint(g);
            Font font = g.getFont();
            font = font.deriveFont(Font.BOLD, 48f);
            FontMetrics fm = g.getFontMetrics(font);
            g.setFont(font);
            g.setColor(Color.RED);
            String text = Integer.toString(maxCars);
            int x = getWidth() - fm.stringWidth(text);
            int y = getHeight() - fm.getHeight() + fm.getAscent();
            g.drawString(text, x, y);
            text = Integer.toString(getComponentCount());
            x = getWidth() - fm.stringWidth(text);
            y -= fm.getHeight();
            g.drawString(text, x, y);
            text = Integer.toString(cars.size());
            x = getWidth() - fm.stringWidth(text);
            y -= fm.getHeight();
            g.drawString(text, x, y);
        

        @Override
        public Dimension getPreferredSize() 
            return new Dimension(400, 400);
        

        public void setCongestion(int value) 
            maxCars = value;
        
    

    protected static Point2D getPointAt(double radius, double angle) 

        double x = Math.round(radius / 2d);
        double y = Math.round(radius / 2d);

        double rads = Math.toRadians(-angle);

        double fullLength = Math.round((radius / 2d));

        double xPosy = (Math.cos(rads) * fullLength);
        double yPosy = (Math.sin(rads) * fullLength);

        return new Point2D.Double(xPosy, yPosy);

    

    public class Car extends JPanel 

        private double direction;
        private double speed;
        private BufferedImage background;

        public Car() 
            setOpaque(false);
            direction = Math.random() * 360;
            speed = 5 + (Math.random() * 10);
            int image = 1 + (int) Math.round(Math.random() * 5);
            try 
                String name = "/Car0" + image + ".png";
                background = ImageIO.read(getClass().getResource(name));
             catch (IOException ex) 
                ex.printStackTrace();
            
            setSize(getPreferredSize());
//            setBorder(new LineBorder(Color.RED));
        

        public void setDirection(double direction) 
            this.direction = direction;
            revalidate();
            repaint();
        

        public double getDirection() 
            return direction;
        

        public void move() 
            Point at = getLocation();
            at.x += (int)(speed * Math.cos(Math.toRadians(-direction)));
            at.y += (int)(speed * Math.sin(Math.toRadians(-direction)));
            setLocation(at);
        

        @Override
        public Dimension getPreferredSize() 
            Dimension size = super.getPreferredSize();
            if (background != null) 
                double radian = Math.toRadians(direction);
                double sin = Math.abs(Math.sin(radian)), cos = Math.abs(Math.cos(radian));
                int w = background.getWidth(), h = background.getHeight();
                int neww = (int) Math.floor(w * cos + h * sin);
                int newh = (int) Math.floor(h * cos + w * sin);
                size = new Dimension(neww, newh);
            
            return size;
        

        @Override
        protected void paintComponent(Graphics g) 
            super.paintComponent(g);
            Graphics2D g2d = (Graphics2D) g.create();
            int x = (getWidth() - background.getWidth()) / 2;
            int y = (getHeight() - background.getHeight()) / 2;
            g2d.rotate(Math.toRadians(-(direction + 180)), getWidth() / 2, getHeight() / 2);
            g2d.drawImage(background, x, y, this);
            g2d.dispose();

//            Debug graphics...
//            int cx = getWidth() / 2;
//            int cy = getHeight() / 2;
//
//            g2d = (Graphics2D) g.create();
//            g2d.setColor(Color.BLUE);
//            double radius = Math.min(getWidth(), getHeight());
//            Point2D pointAt = getPointAt(radius, direction);
//            g2d.draw(new Ellipse2D.Double(cx - (radius / 2d), cy - (radius / 2d), radius, radius));
//            
//            double xo = cx;
//            double yo = cy;
//            double xPos = cx + pointAt.getX();
//            double yPos = cy + pointAt.getY();
//            
//            g2d.draw(new Line2D.Double(xo, yo, xPos, yPos));
//            g2d.draw(new Ellipse2D.Double(xPos - 2, yPos - 2, 4, 4));
//            g2d.dispose();
        
    

更新为优化版本

我对汽车对象的创建进行了一些代码优化(仍有改进的空间)并增强了图形输出(使它看起来更好)。

基本上,现在,当汽车离开屏幕时,它会被放置在水池中。当需要另一辆车时,如果可能,将其从池中拉出,否则将制造一辆新车。这减少了创建和销毁这么多(相对)短寿命对象的开销,从而使内存使用更加稳定。

在我的 2560x1600 分辨率屏幕(最大化运行)上,我可以让 4500 辆汽车同时运行。一旦减少了对象创建,它就会相对平稳地运行(它永远不会像 10 一样运行,但它并没有受到速度显着降低的影响)。

public class TestAnimation10 

    public static void main(String[] args) 
        new TestAnimation10();
    

    public TestAnimation10() 
        EventQueue.invokeLater(new Runnable() 
            @Override
            public void run() 
                try 
                    UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName());
                 catch (Exception ex) 
                

                final TrackPane trackPane = new TrackPane();
                JSlider slider = new JSlider(1, 5000);
                slider.addChangeListener(new ChangeListener() 
                    @Override
                    public void stateChanged(ChangeEvent e) 
                        trackPane.setCongestion(((JSlider) e.getSource()).getValue());
                    
                );
                slider.setValue(5);

                JFrame frame = new JFrame("Test");
                frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
                frame.setLayout(new BorderLayout());
                frame.add(trackPane);
                frame.add(slider, BorderLayout.SOUTH);
                frame.pack();
                frame.setLocationRelativeTo(null);
                frame.setVisible(true);

            
        );
    

    public class TrackPane extends JPanel 

        private List<Car> activeCarList;
        private List<Car> carPool;
        private int maxCars = 1;
        private List<Point2D[]> points;
        private Ellipse2D areaOfEffect;

        public TrackPane() 

            points = new ArrayList<>(25);

            activeCarList = new ArrayList<>(25);
            carPool = new ArrayList<>(25);
            setLayout(null);

            Timer timer = new Timer(40, new ActionListener() 
                @Override
                public void actionPerformed(ActionEvent e) 

                    Rectangle bounds = areaOfEffect.getBounds();
                    List<Car> tmp = new ArrayList<>(activeCarList);
                    for (Car car : tmp) 
                        car.move();
                        if (!bounds.intersects(car.getBounds())) 
                            remove(car);
                            activeCarList.remove(car);
                            carPool.add(car);
                        
                    
                    updatePool();
                    repaint();
                
            );

            timer.setRepeats(true);
            timer.setCoalesce(true);
            timer.start();

            updateAreaOfEffect();
        

        protected void updateAreaOfEffect() 
            double radius = Math.max(getWidth(), getHeight()) * 1.5d;
            double x = (getWidth() - radius) / 2d;
            double y = (getHeight() - radius) / 2d;
            areaOfEffect = new Ellipse2D.Double(x, y, radius, radius);
        

        @Override
        public void invalidate() 
//            super.invalidate();
            updateAreaOfEffect();
        

        protected void updatePool() 
            if (activeCarList.size() < maxCars) 
                int count = Math.min(maxCars - activeCarList.size(), 10);
                for (int index = 0; index < count; index++) 
                    Car car = null;

                    if (carPool.isEmpty()) 
                        car = new Car();
                     else 
                        car = carPool.remove(0);
                    

                    double direction = car.getDirection();
                    double startAngle = direction - 180;

                    double radius = areaOfEffect.getWidth();
                    Point2D startPoint = getPointAt(radius, startAngle);

                    int cx = getWidth() / 2;
                    int cy = getHeight() / 2;

                    double x = cx + (startPoint.getX() - car.getWidth() / 2);
                    double y = cy + (startPoint.getY() - car.getHeight() / 2);
                    car.setLocation((int) x, (int) y);

                    Point2D targetPoint = getPointAt(radius, direction);

                    points.add(new Point2D[]startPoint, targetPoint);

                    add(car);

                    activeCarList.add(car);
                
            
        

        @Override
        public void paint(Graphics g) 
            super.paint(g);
            Font font = g.getFont();
            font = font.deriveFont(Font.BOLD, 48f);
            FontMetrics fm = g.getFontMetrics(font);
            g.setFont(font);
            g.setColor(Color.RED);
            String text = Integer.toString(maxCars);
            int x = getWidth() - fm.stringWidth(text);
            int y = getHeight() - fm.getHeight() + fm.getAscent();
            g.drawString(text, x, y);
            text = Integer.toString(getComponentCount());
            x = getWidth() - fm.stringWidth(text);
            y -= fm.getHeight();
            g.drawString(text, x, y);
            text = Integer.toString(activeCarList.size());
            x = getWidth() - fm.stringWidth(text);
            y -= fm.getHeight();
            g.drawString(text, x, y);
            text = Integer.toString(carPool.size());
            x = getWidth() - fm.stringWidth(text);
            y -= fm.getHeight();
            g.drawString(text, x, y);
        

        @Override
        public Dimension getPreferredSize() 
            return new Dimension(400, 400);
        

        public void setCongestion(int value) 
            maxCars = value;
        

        @Override
        public void validate() 
        

        @Override
        public void revalidate() 
        

//        @Override
//        public void repaint(long tm, int x, int y, int width, int height) 
//        
//
//        @Override
//        public void repaint(Rectangle r) 
//        
//        public void repaint() 
//        
        @Override
        protected void firePropertyChange(String propertyName, Object oldValue, Object newValue) 
            System.out.println(propertyName);
//            // Strings get interned...
//            if (propertyName == "text"
//                            || propertyName == "labelFor"
//                            || propertyName == "displayedMnemonic"
//                            || ((propertyName == "font" || propertyName == "foreground")
//                            && oldValue != newValue
//                            && getClientProperty(javax.swing.plaf.basic.Basichtml.propertyKey) != null)) 
//
//                super.firePropertyChange(propertyName, oldValue, newValue);
//            
        

        @Override
        public void firePropertyChange(String propertyName, boolean oldValue, boolean newValue) 
        
    

    protected static Point2D getPointAt(double radius, double angle) 

        double x = Math.round(radius / 2d);
        double y = Math.round(radius / 2d);

        double rads = Math.toRadians(-angle);

        double fullLength = Math.round((radius / 2d));

        double xPosy = (Math.cos(rads) * fullLength);
        double yPosy = (Math.sin(rads) * fullLength);

        return new Point2D.Double(xPosy, yPosy);

    

    public class Car extends JPanel 

        private double direction;
        private double speed;
        private BufferedImage background;

        public Car() 
            setOpaque(false);
            direction = Math.random() * 360;
            speed = 5 + (Math.random() * 10);
            int image = 1 + (int) Math.round(Math.random() * 5);
            try 
                String name = "/Car0" + image + ".png";
                background = ImageIO.read(getClass().getResource(name));
             catch (IOException ex) 
                ex.printStackTrace();
            
            setSize(getPreferredSize());
//            setBorder(new LineBorder(Color.RED));
        

        public void setDirection(double direction) 
            this.direction = direction;
            revalidate();
            repaint();
        

        public double getDirection() 
            return direction;
        

        public void move() 
            Point at = getLocation();
            at.x += (int) (speed * Math.cos(Math.toRadians(-direction)));
            at.y += (int) (speed * Math.sin(Math.toRadians(-direction)));
            setLocation(at);
        

        @Override
        public Dimension getPreferredSize() 
            Dimension size = super.getPreferredSize();
            if (background != null) 
                double radian = Math.toRadians(direction);
                double sin = Math.abs(Math.sin(radian)), cos = Math.abs(Math.cos(radian));
                int w = background.getWidth(), h = background.getHeight();
                int neww = (int) Math.floor(w * cos + h * sin);
                int newh = (int) Math.floor(h * cos + w * sin);
                size = new Dimension(neww, newh);
            
            return size;
        

        @Override
        protected void paintComponent(Graphics g) 
            super.paintComponent(g);
            Graphics2D g2d = (Graphics2D) g.create();
            g2d.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);
            g2d.setRenderingHint(RenderingHints.KEY_ALPHA_INTERPOLATION, RenderingHints.VALUE_ALPHA_INTERPOLATION_QUALITY);
            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 x = (getWidth() - background.getWidth()) / 2;
            int y = (getHeight() - background.getHeight()) / 2;
            g2d.rotate(Math.toRadians(-(direction + 180)), getWidth() / 2, getHeight() / 2);
            g2d.drawImage(background, x, y, this);
            g2d.dispose();

//            Debug graphics...
//            int cx = getWidth() / 2;
//            int cy = getHeight() / 2;
//
//            g2d = (Graphics2D) g.create();
//            g2d.setColor(Color.BLUE);
//            double radius = Math.min(getWidth(), getHeight());
//            Point2D pointAt = getPointAt(radius, direction);
//            g2d.draw(new Ellipse2D.Double(cx - (radius / 2d), cy - (radius / 2d), radius, radius));
//            
//            double xo = cx;
//            double yo = cy;
//            double xPos = cx + pointAt.getX();
//            double yPos = cy + pointAt.getY();
//            
//            g2d.draw(new Line2D.Double(xo, yo, xPos, yPos));
//            g2d.draw(new Ellipse2D.Double(xPos - 2, yPos - 2, 4, 4));
//            g2d.dispose();
        

        @Override
        public void invalidate() 
        

        @Override
        public void validate() 
        

        @Override
        public void revalidate() 
        

        @Override
        public void repaint(long tm, int x, int y, int width, int height) 
        

        @Override
        public void repaint(Rectangle r) 
        

        @Override
        public void repaint() 
        

        @Override
        protected void firePropertyChange(String propertyName, Object oldValue, Object newValue) 
//            System.out.println(propertyName);
//            // Strings get interned...
//            if (propertyName == "text"
//                            || propertyName == "labelFor"
//                            || propertyName == "displayedMnemonic"
//                            || ((propertyName == "font" || propertyName == "foreground")
//                            && oldValue != newValue
//                            && getClientProperty(javax.swing.plaf.basic.BasicHTML.propertyKey) != null)) 
//
//                super.firePropertyChange(propertyName, oldValue, newValue);
//            
        

        @Override
        public void firePropertyChange(String propertyName, boolean oldValue, boolean newValue) 
        
    

ps - 我应该添加 1- 我 10 个月大的孩子喜欢它 2- 它让我想起了跑步去上班:P

【讨论】:

+1 乐趣;还与RobotChase 演员一起娱乐。 @MadProgrammer - 谢谢。将研究你的代码,看看我能从中学到什么(可能很多!)。只是出于好奇,您使用 Swing 编程有多久了?我一个月前才刚开始,但发现它让人不知所措,因为有很多不同的做事方式,有时很难弄清楚什么是最好的等等。这只是我遇到的问题,还是一般来说新手体验?我希望有一天我能像你这样的程序员和垃圾神一样优秀,即使现在看起来很遥远! 我从 Java 1.3 开始使用 Java/Swing,大约在 1999 年左右。每个开发人员一直面临的问题,事实上,我认为你知道的越多,它就越糟糕得到。不同之处在于,您倾向于体验能够更快地丢弃某些想法。不要害怕尝试并丢弃它。你能做的最糟糕的事情就是继续沿着这条路走下去,仅仅因为你已经投入了太多时间。如果你能有更好的方法,或许重新开始会更好

以上是关于摇摆动画运行极慢的主要内容,如果未能解决你的问题,请参考以下文章

摇摆动画暂停和恢复

svg.js元素的动画旋转给出了意想不到的结果(可见“摇摆”)

如何无限向前运行CSS动画

将 CSS3 变换/动画与 font-face 一起使用会产生类似“摇摆不定”的微调器 gif

你如何让图像在 iPhone 主屏幕上摇摆不定?

Java GIF 动画未正确重绘