java - 防止摆动动画跳跃
问题描述
我已经使用 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();
解决方案
您可以通过以下方式实现更平滑的动画: - 使用更精细的 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 );
}
}
推荐阅读
- javascript - 导入失败 - 未捕获的 ReferenceError:未定义函数
- javascript - 在 Javascript 中隐藏图像的不同方法
- swift - 不能对“Auth”类型的非可选值使用可选链接
- django - Django Oracle 插入
- haskell - 如何在不解析错误的情况下使用我的函数转换特定字符?
- javascript - Vuetify 的日期选择器不显示日期
- sql - 将逗号分隔的值拆分为多行 - Oracle SQL
- ruby - 使用不同的参数多次运行 rspec 测试套件
- ios - 按钮图像未显示在 UItextfield 的 rightView 中
- javascript - 在 js 函数中处理玩家轮流检查的更好方法?