java中用于快速移动对象的平滑动画
我正在创建一个简单的动画,球以不同的速度从屏幕的一侧移动到另一侧。问题是,随着球的速度加快,我可以看到球明显的闪烁,实际上这很难解释,但当球的一部分仍在前一步时,我可以看到重新绘制 我尝试了很多东西,包括:java中用于快速移动对象的平滑动画,java,animation,javafx,Java,Animation,Javafx,我正在创建一个简单的动画,球以不同的速度从屏幕的一侧移动到另一侧。问题是,随着球的速度加快,我可以看到球明显的闪烁,实际上这很难解释,但当球的一部分仍在前一步时,我可以看到重新绘制 我尝试了很多东西,包括: 本机swing动画使用第一个线程/sleep/repain,然后移动到计时器 切换到swing jframe内的javafx画布/窗格。尝试了过渡和动画计时器 修补CreateBufferStrategy,对于1,2,3-老实说,没有看到任何区别(可能我做错了什么…) 我的问题是如何提高平滑
import java.awt.Dimension;
import java.awt.GraphicsDevice;
import java.awt.GraphicsEnvironment;
import javafx.animation.Interpolator;
import javafx.animation.Timeline;
import javafx.animation.TranslateTransition;
import javafx.application.Platform;
import javafx.embed.swing.JFXPanel;
import javafx.scene.Group;
import javafx.scene.Scene;
import javafx.scene.canvas.Canvas;
import javafx.scene.paint.Color;
import javafx.scene.shape.Circle;
import javafx.util.Duration;
import javax.swing.JFrame;
public class FXTrackerPanel extends JFrame {
/**
*
*/
private static final long serialVersionUID = 1L;
public int crSize = 30;
public double xPos = crSize;
public double yPos = 100;
public int xSize = 100;
public int ySize = 100;
public Circle r;
int dir = 1;
public void updateScreenSize() {
int screen = 0;
GraphicsEnvironment ge = GraphicsEnvironment
.getLocalGraphicsEnvironment();
GraphicsDevice[] gs = ge.getScreenDevices();
if( screen > -1 && screen < gs.length )
{
xSize = gs[screen].getDisplayMode().getWidth();
ySize = gs[screen].getDisplayMode().getHeight();
}
else if( gs.length > 0 )
{
xSize = gs[0].getDisplayMode().getWidth();
ySize = gs[0].getDisplayMode().getHeight();
}
else
{
throw new RuntimeException( "No Screens Found" );
}
yPos = ySize / 2;
}
private void initFXPanel(JFXPanel fxPanel) {
updateScreenSize();
xPos = crSize;
Group root = new Group();
double speed = 5;
int repeats = Timeline.INDEFINITE;
r = new javafx.scene.shape.Circle(xPos, yPos, crSize / 2, Color.RED);
TranslateTransition tt = new TranslateTransition(Duration.seconds(speed), r);
tt.setFromX(xPos);
tt.setToX(xSize - crSize * 3);
tt.setCycleCount(repeats);
tt.setAutoReverse(true);
tt.setInterpolator(Interpolator.EASE_BOTH);
tt.play();
root.getChildren().add(r);
// new AnimationTimer() {
//
// @Override
// public void handle(long now) {
// double speed = 20;
// try {
// speed = Double.valueOf(TETSimple.mp.speedSinus.getText());
// }
// catch (Exception ex) {
// speed = 20;
// }
// double xMov = (speed * 4 * Math.sin( xPos * Math.PI / xSize ) );
// if (xMov <= 0) {
// xMov = 1;
// }
// if (dir == 1) {
// if (xPos >= xSize - crSize)
// dir = 0;
// xPos += xMov;
// } else {
// if (xPos <= 1)
// dir = 1;
// xPos -= xMov;
// }
//
// r.setTranslateX(xPos);
// }
// }.start();
fxPanel.setScene(new Scene(root));
}
public FXTrackerPanel() {
updateScreenSize();
this.setSize(new Dimension(xSize, ySize));
this.setPreferredSize(new Dimension(xSize, ySize));
this.setVisible(true);
this.setDefaultCloseOperation(DISPOSE_ON_CLOSE);
JFXPanel fxPanel = new JFXPanel();
this.add(fxPanel);
this.createBufferStrategy(3);
Platform.runLater(new Runnable() {
@Override
public void run() {
initFXPanel(fxPanel);
}
});
}
public static void main(String[] args)
{
new FXTrackerPanel();
}
}
导入java.awt.Dimension;
导入java.awt.GraphicsDevice;
导入java.awt.GraphicsEnvironment;
导入javafx.animation.Interpolator;
导入javafx.animation.Timeline;
导入javafx.animation.TranslateTransition;
导入javafx.application.Platform;
导入javafx.embed.swing.JFXPanel;
导入javafx.scene.Group;
导入javafx.scene.scene;
导入javafx.scene.canvas.canvas;
导入javafx.scene.paint.Color;
导入javafx.scene.shape.Circle;
导入javafx.util.Duration;
导入javax.swing.JFrame;
公共类FXTrackerPanel扩展JFrame{
/**
*
*/
私有静态最终长serialVersionUID=1L;
公共int crSize=30;
公共双XPO=crSize;
公共双YPO=100;
公共int xSize=100;
公共实体=100;
公众圈;
int dir=1;
public void updateScreenSize(){
int屏幕=0;
GraphicsEnvironment ge=GraphicsEnvironment
.getLocalGraphicsEnvironment();
GraphicsDevice[]gs=ge.getScreenDevices();
如果(屏幕>-1&&screen0)
{
xSize=gs[0].getDisplayMode().getWidth();
ySize=gs[0].getDisplayMode().getHeight();
}
其他的
{
抛出新的RuntimeException(“未找到屏幕”);
}
yPos=ySize/2;
}
私有void initFXPanel(JFXPanel fxPanel){
updateScreenSize();
xPos=crSize;
组根=新组();
双速=5;
int repeats=Timeline.unfinite;
r=新的javafx.scene.shape.Circle(xPos、yPos、crSize/2、Color.RED);
TranslateTransition tt=新的TranslateTransition(持续时间。秒(速度),r);
tt.setFromX(xPos);
tt.setToX(xSize-crSize*3);
tt.setCycleCount(重复);
tt.setAutoReverse(真);
tt.setInterpolator(Interpolator.EASE_两者);
tt.play();
root.getChildren().add(r);
//新的AnimationTimer(){
//
//@覆盖
//公共无效句柄(长){
//双速=20;
//试一试{
//speed=Double.valueOf(TETSimple.mp.speedSinus.getText());
// }
//捕获(例外情况除外){
//速度=20;
// }
//double xMov=(速度*4*Math.sin(xPos*Math.PI/xSize));
//if(xMov=xSize-crSize)
//dir=0;
//xPos+=xMov;
//}其他{
//如果(xPos-1&&屏幕长度0){
xSize=gs[0].getDisplayMode().getWidth();
ySize=gs[0].getDisplayMode().getHeight();
}否则{
抛出新的RuntimeException(“未找到屏幕”);
}
yPos=ySize/2;
yPosPrev=ySize/2;
}
已执行的公共无效操作(操作事件arg0){
if(方法==1)
线条运动();
其他的
正弦运动();
重新绘制(0,ySize/2,xSize,crSize);
}
专用双解析文本2int(字符串文字){
试一试{
返回Double.valueOf(literal);
}捕获(例外情况除外){
例如printStackTrace();
}
返回10.0;
}
私有void checkFinishCondition(){
如果(通过+1>重复&&repeats!=0){
如果(!单击关闭){
TETSimple.mp.bStop.doClick();
clickedClose=true;
}
回来
}
}
私人行动{
此参数为.updateScreenSize();
this.repeats=parseText2Int(TETSimple.mp.repeatsCount.getText()).intValue();
checkFinishCondition();
double speed=parseText2Int(TETSimple.mp.speedSinus.getText());
double xMov=(speed*Math.sin(xPos*Math.PI/xSize));
if(xMov=xSize-crSize)
dir=0;
xPosPrev=xPos;
xPos+=xMov;
}否则{
如果(xPos我认为问题是由AWT中呈现的JFXPanel
引起的:在幕后发生了一些复杂的事情,需要在两个不同的系统线程(AWT事件调度线程和FX应用程序线程)之间进行同步
如果您可以将其编写为一个“纯”JavaFX应用程序(即没有Swing/AWT代码),那么它运行起来会更平稳:
import javafx.animation.Interpolator;
import javafx.animation.Timeline;
import javafx.animation.TranslateTransition;
import javafx.application.Application;
import javafx.geometry.Rectangle2D;
import javafx.scene.Group;
import javafx.scene.Scene;
import javafx.scene.paint.Color;
import javafx.scene.shape.Circle;
import javafx.stage.Screen;
import javafx.stage.Stage;
import javafx.util.Duration;
public class FXAnimationTest extends Application {
@Override
public void start(Stage primaryStage) {
Group root = new Group();
double speed = 5;
int repeats = Timeline.INDEFINITE;
Screen screen = Screen.getPrimary();
Rectangle2D screenBounds = screen.getBounds();
double xSize = screenBounds.getWidth();
double ySize = screenBounds.getHeight();
double crSize = 30 ;
double xPos = crSize ;
double yPos = ySize / 2 ;
Circle r = new Circle(xPos, yPos, crSize / 2, Color.RED);
TranslateTransition tt = new TranslateTransition(Duration.seconds(speed), r);
tt.setFromX(xPos);
tt.setToX(xSize - crSize * 3);
tt.setCycleCount(repeats);
tt.setAutoReverse(true);
tt.setInterpolator(Interpolator.EASE_BOTH);
tt.play();
root.getChildren().add(r);
Scene scene = new Scene(root, xSize, ySize);
primaryStage.setScene(scene);
primaryStage.show();
}
public static void main(String[] args) {
launch(args);
}
}
如果您必须在Swing应用程序中嵌入JavaFX,并且您使用的是Java 8(我在8u20上测试过),那么有一个系统属性可以在同一线程上运行两个UI工具包。我认为这目前仍然是实验性的,因此使用风险自担,
import javafx.animation.Interpolator;
import javafx.animation.Timeline;
import javafx.animation.TranslateTransition;
import javafx.application.Application;
import javafx.geometry.Rectangle2D;
import javafx.scene.Group;
import javafx.scene.Scene;
import javafx.scene.paint.Color;
import javafx.scene.shape.Circle;
import javafx.stage.Screen;
import javafx.stage.Stage;
import javafx.util.Duration;
public class FXAnimationTest extends Application {
@Override
public void start(Stage primaryStage) {
Group root = new Group();
double speed = 5;
int repeats = Timeline.INDEFINITE;
Screen screen = Screen.getPrimary();
Rectangle2D screenBounds = screen.getBounds();
double xSize = screenBounds.getWidth();
double ySize = screenBounds.getHeight();
double crSize = 30 ;
double xPos = crSize ;
double yPos = ySize / 2 ;
Circle r = new Circle(xPos, yPos, crSize / 2, Color.RED);
TranslateTransition tt = new TranslateTransition(Duration.seconds(speed), r);
tt.setFromX(xPos);
tt.setToX(xSize - crSize * 3);
tt.setCycleCount(repeats);
tt.setAutoReverse(true);
tt.setInterpolator(Interpolator.EASE_BOTH);
tt.play();
root.getChildren().add(r);
Scene scene = new Scene(root, xSize, ySize);
primaryStage.setScene(scene);
primaryStage.show();
}
public static void main(String[] args) {
launch(args);
}
}
java -Djavafx.embed.singleThread=true FXTrackerPanel
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.GridBagConstraints;
import java.awt.GridBagLayout;
import java.awt.RenderingHints;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.util.ArrayList;
import java.util.List;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JPanel;
import javax.swing.JSlider;
import javax.swing.Timer;
import javax.swing.UIManager;
import javax.swing.UnsupportedLookAndFeelException;
import javax.swing.event.ChangeEvent;
import javax.swing.event.ChangeListener;
public class BouncyBall {
public static void main(String[] args) {
new BouncyBall();
}
public BouncyBall() {
EventQueue.invokeLater(new Runnable() {
@Override
public void run() {
try {
UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName());
} catch (ClassNotFoundException | InstantiationException | IllegalAccessException | UnsupportedLookAndFeelException ex) {
ex.printStackTrace();
}
JFrame frame = new JFrame("Testing");
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.add(new ControlPane());
frame.pack();
frame.setLocationRelativeTo(null);
frame.setVisible(true);
}
});
}
public class ControlPane extends JPanel {
private JSlider speed;
private JSlider quanity;
private BallPitPane ballPitPane;
public ControlPane() {
setLayout(new BorderLayout());
ballPitPane = new BallPitPane();
add(ballPitPane);
JPanel controls = new JPanel(new GridBagLayout());
GridBagConstraints gbc = new GridBagConstraints();
gbc.gridx = 0;
gbc.gridy = 0;
gbc.anchor = GridBagConstraints.WEST;
speed = new JSlider(1, 100, 4);
quanity = new JSlider(1, 100, 1);
controls.add(new JLabel("Speed:"), gbc);
gbc.gridy++;
controls.add(new JLabel("Quanity:"), gbc);
gbc.gridx++;
gbc.gridy = 0;
gbc.weightx = 1;
gbc.fill = GridBagConstraints.HORIZONTAL;
controls.add(speed, gbc);
gbc.gridy++;
controls.add(quanity, gbc);
add(controls, BorderLayout.SOUTH);
speed.addChangeListener(new ChangeListener() {
@Override
public void stateChanged(ChangeEvent e) {
ballPitPane.setSpeed(speed.getValue());
}
});
quanity.addChangeListener(new ChangeListener() {
@Override
public void stateChanged(ChangeEvent e) {
ballPitPane.setQuanity(quanity.getValue());
}
});
}
}
public class BallPitPane extends JPanel {
private List<Ball> balls;
private int speed;
public BallPitPane() {
balls = new ArrayList<>(25);
setSpeed(2);
setQuanity(1);
Timer timer = new Timer(40, new ActionListener() {
@Override
public void actionPerformed(ActionEvent e) {
for (Ball ball : balls) {
ball.update(getWidth(), speed);
}
repaint();
}
});
timer.start();
}
public void setSpeed(int speed) {
this.speed = speed;
}
public void setQuanity(int quanity) {
while (balls.size() > quanity) {
balls.remove(0);
}
while (balls.size() < quanity) {
int radius = 4 + (int) (Math.random() * 48);
Ball ball = new Ball(
randomColor(),
(int) Math.abs(Math.random() * getWidth() - radius),
(int) Math.abs(Math.random() * getHeight() - radius),
radius
);
balls.add(ball);
}
}
protected Color randomColor() {
int red = (int) Math.abs(Math.random() * 255);
int green = (int) Math.abs(Math.random() * 255);
int blue = (int) Math.abs(Math.random() * 255);
return new Color(red, green, blue);
}
@Override
public Dimension getPreferredSize() {
return new Dimension(400, 200);
}
@Override
protected void paintComponent(Graphics g) {
super.paintComponent(g);
Graphics2D g2d = (Graphics2D) g.create();
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);
for (Ball ball : balls) {
ball.paint(g2d);
}
g2d.dispose();
}
public class Ball {
private Color color;
private int x;
private int y;
private int radius;
private int delta;
public Ball(Color color, int x, int y, int radius) {
this.color = color;
this.x = x;
this.y = y;
this.radius = radius;
delta = Math.random() > 0.5 ? 1 : -1;
}
public void update(int width, int speed) {
x += speed * delta;
if (x + radius >= width) {
x = width - radius;
delta *= -1;
} else if (x < 0) {
x = 0;
delta *= -1;
}
}
public void paint(Graphics g) {
g.setColor(color);
g.fillOval(x, y, radius, radius);
}
}
}
}