首页 > 解决方案 > 防止摆动动画跳跃

问题描述

我已经使用 aJPanel和 a制作了这个基本示例,javax.swing.Timer我希望动画相对流畅。

如果我让鼠标在窗口上移动,动画很流畅。如果我根本不与窗口交互,那么动画就会开始跳跃。

import javax.swing.*;
import java.awt.*;

public class SmoothSwing{
    double x = 0;
    double y = 0;
    double theta = 0;

    public void step(){
        x = 175 + 175*Math.sin( theta );
        y = 175 + 175*Math.cos( theta );
        theta += 0.02;
        if(theta > 6.28) theta = 0;
    }

    public void buildGui(){
        JFrame frame = new JFrame("smooth");
        JPanel panel = new JPanel(){
            Dimension dim = new Dimension(400, 400);
            @Override
            public void paintComponent(Graphics g){
                super.paintComponent(g);
                g.drawOval((int)x, (int)y, 50, 50);
            }
            @Override
            public Dimension getMinimumSize(){
                return dim;
            }
            @Override
            public Dimension getPreferredSize(){
                return dim;
            }
        };
        frame.add(panel, BorderLayout.CENTER);
        frame.setVisible(true);
        frame.pack();
        frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
        Timer timer = new Timer(30, evt->{
            step();
            panel.repaint();
        });
        timer.start();
    }
    public static void main(String[] args){
        EventQueue.invokeLater( new SmoothSwing()::buildGui );
    }

}

在我看来,重绘的东西正在累积并成团重绘。所以我注释掉了super.paintComponent(g);会导致面板不被清除的行。

没有清除圆圈的屏幕

当我这样做时,我可以看到paintComponent 正在运行,因为所有的圆圈都在被绘制,但显示仍然只在绘制多个圆圈后更新。

我在使用 Linux 的 jdk-14+36、jdk-11.0.6 和 1.8.0_181 上遇到了这个问题。Ubuntu 18.04。

这是比较差异的图形。我无法控制 gif 的速度,但这说明了正在发生的事情。

预期的行为。 短暂不活动后。 又过了一会。

从左到右,如果我将鼠标移到窗口上,我会得到左侧的行为,在我停止后它会变得有点跳跃(中间)然后它会变得更加跳跃(右)。

为了避免这个问题,我可以做的一件事是将我的计时器分成两个任务。

Timer t1 = new Timer(30, evt->step());
Timer t2 = new Timer(3, evt->panel.repaint());
t1.start();
t2.start();

标签: javaswing

解决方案


您可以通过以下方式实现更平滑的动画: - 使用更精细的 theta 步长 - 以更高的频率更新 - 使用双精度值进行绘制:

import java.awt.BorderLayout;
import java.awt.Dimension;
import java.awt.EventQueue;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.RenderingHints;
import java.awt.geom.Ellipse2D;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.Timer;

public class SmoothSwing{
    double x = 0;
    double y = 0;
    double theta = 0;

    public void step(){
        x = 175 + 175*Math.sin( theta );
        y = 175 + 175*Math.cos( theta );
        theta += 0.002; //finer theta step
        if(theta > 6.28) {
            theta = 0;
        }
    }

    public void buildGui(){
        JFrame frame = new JFrame("smooth");
        JPanel panel = new JPanel(){
            Dimension dim = new Dimension(400, 400);
            @Override
            public void paintComponent(Graphics g){
                super.paintComponent(g);
                Graphics2D g2d = (Graphics2D) g;
                g2d.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);
                g2d.setRenderingHint(RenderingHints.KEY_STROKE_CONTROL, RenderingHints.VALUE_STROKE_PURE);
                g2d.draw(new Ellipse2D.Double(x, y, 50, 50));
            }
            @Override
            public Dimension getMinimumSize(){
                return dim;
            }
            @Override
            public Dimension getPreferredSize(){
                return dim;
            }
        };
        frame.add(panel, BorderLayout.CENTER);
        frame.setVisible(true);
        frame.pack();
        frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
        Timer timer = new Timer(3, evt->{ //higher frequency update
            step();
            panel.repaint();
        });
        timer.start();
    }
    public static void main(String[] args){
        EventQueue.invokeLater( new SmoothSwing()::buildGui );
    }   
}

推荐阅读