【发布时间】:2015-02-25 05:36:38
【问题描述】:
我正在创建球以不同速度从屏幕一侧移动到另一侧的简单动画。问题是,在球的速度较高时,我可以看到球的明显闪烁,实际上很难解释,但是当部分球仍在上一步时,我可以看到重绘。
我尝试了很多方法,包括:
原生摇摆动画使用第一个线程/睡眠/repain,然后移至计时器
在 swing jframe 中切换到 javafx 画布/窗格。尝试了过渡和 AnimationTimer
修补 CreateBufferStrategy,对于 1、2、3 - 老实说,没有看到任何区别(也许我做错了什么......)
我的问题是如何提高平滑度以及我想要实现的目标是否可以使用本机 java 或者使用一些外部库更好?如果可以,你能推荐一些东西吗?
下面显示了我的第二/第三次尝试的示例代码。
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();
}
}
这里是swing代码的例子:
import java.awt.Color;
import java.awt.Dimension;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.GraphicsDevice;
import java.awt.GraphicsEnvironment;
import java.awt.RenderingHints;
import java.awt.Shape;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.geom.Ellipse2D;
import javax.swing.JPanel;
import javax.swing.Timer;
import java.lang.Math;
import java.util.Random;
public class TrackerPanel extends JPanel implements ActionListener {
/**
*
*/
private static final long serialVersionUID = 1L;
Shape cr;
Color c;
public int crSize = 30;
public double xPos = crSize;
public double yPos = 100;
public double xPosPrev = crSize;
public double yPosPrev = 100;
public int xSize = 100;
public int ySize = 100;
int dir = 1; // left
int timer = 50; // 50 - sins, 1500 - linear
int method = 1; // 1 - jump, 2 - sinus
int timeToChange = 1000;
int passes = 0;
Timer tt;
boolean clickedClose = false;
private int repeats = 0;
// t - timer interval, m - method of ball movement: 1 - jump, 2 - sinus
public TrackerPanel(int t, int m) {
this.setPreferredSize(new Dimension(300, 200));
this.timer = t;
this.method = m;
c = Color.red;
repaint();
this.updateScreenSize();
tt = new Timer(t, null);
tt.addActionListener(this);
tt.start();
}
public void updateScreenSize() {
int screen = TETSimple.suppMonitor;
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;
yPosPrev = ySize / 2;
}
public void actionPerformed(ActionEvent arg0) {
if (method == 1)
lineMovement();
else
sinusMovement();
repaint(0, ySize / 2, xSize, crSize);
}
private Double parseText2Int(String literal) {
try {
return Double.valueOf(literal);
} catch (Exception ex) {
ex.printStackTrace();
}
return 10.0;
}
private void checkFinishCondition() {
if (passes + 1 > repeats && repeats != 0) {
if (!clickedClose) {
TETSimple.mp.bStop.doClick();
clickedClose = true;
}
return;
}
}
private void sinusMovement() {
this.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 <= 0) {
xMov = 1;
}
if (dir == 1) {
if (xPos >= xSize - crSize)
dir = 0;
xPosPrev = xPos;
xPos += xMov;
} else {
if (xPos <= 1 + crSize) {
dir = 1;
passes++;
}
xPosPrev = xPos;
xPos -= xMov;
}
}
private void lineMovement() {
this.repeats = parseText2Int(TETSimple.mp.repeatsCount.getText()).intValue();
checkFinishCondition();
double left = crSize;
double center = xSize / 2 - crSize * 1.5;
double right = xSize - crSize * 2;
Random r = new Random();
if (timeToChange <= 0) {
passes++;
if (xPos == left || xPos == right) {
timeToChange = 300 + r.nextInt(12) * 100;
xPos = center;
} else if (xPos == center) {
timeToChange = 300 + r.nextInt(7) * 100;
if (r.nextBoolean())
xPos = left;
else
xPos = right;
}
} else {
timeToChange -= 100;
}
}
public 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);
g2d.setColor(Color.green);
g2d.fill(new Ellipse2D.Double(xPos, yPos, crSize, crSize));
g2d.dispose();
}
}
【问题讨论】:
-
闪烁可能是由于不正确地更新输出而不使用适当的双缓冲或在尝试优化更新过程时更新屏幕的错误部分引起的
-
您是否尝试过使用 BufferCapabilities 检查硬件上实际可用的内容?您的双缓冲和页面翻转很可能被忽略。在这种情况下,您需要自己动手。