问题描述
我正在使用Java创建一个动画幻灯片过渡,它在我当前的MacBook Pro和我的一年级iMac上,在Java 6,7和8上都不稳定。
I'm creating an animated slide transition in Java, and it is choppy on my current model MacBook Pro and on my year-old iMac, on Java 6, 7, and 8.
如何让这个动画在Mac OS X上对用户更流畅?
What can I do to make this animation appear smoother to the user on Mac OS X?
import javax.swing.*;
import java.awt.*;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
public class ScratchSpace {
public static void main(String[] args) {
AnimatedPanel panel = new AnimatedPanel();
JFrame frame = new JFrame();
frame.setContentPane(panel);
frame.pack();
frame.setLocationRelativeTo(null);
frame.setVisible(true);
panel.animate();
}
public static class AnimatedPanel extends JPanel {
private float progress = 0.0f; // a number between 0.0 and 1.0
public AnimatedPanel() {
setPreferredSize(new Dimension(800, 600));
setOpaque(true);
}
public void animate() {
final int animationTime = 1000;
int framesPerSecond = 30;
int delay = 1000 / framesPerSecond;
final long start = System.currentTimeMillis();
final Timer timer = new Timer(delay, null);
timer.addActionListener(new ActionListener() {
@Override
public void actionPerformed(ActionEvent e) {
final long now = System.currentTimeMillis();
final long elapsed = now - start;
progress = (float) elapsed / animationTime;
repaint();
if (elapsed >= animationTime) {
timer.stop();
}
}
});
timer.start();
}
@Override
protected void paintComponent(Graphics g) {
Graphics2D g2d = (Graphics2D) g;
int width = getWidth();
int progressWidth = (int) (width * progress);
g2d.setColor(Color.BLUE);
g2d.fillRect(0, 0, progressWidth, getHeight());
g2d.setColor(Color.RED);
g2d.fillRect(progressWidth, 0, width-progressWidth, getHeight());
}
}
}
推荐答案
很大程度上取决于你最终想要实现的目标。
A lot depends on what it is you ultimately want to achieve.
请记住,动画就是运动的错觉......
Remember, animation is the illusion of movement...
我改变了
-
framesPerSecond
改为60
这似乎有所帮助。 - 降低可打印区域的整体高度
- 计算出更改区域和简单称为
重绘(矩形)
仅传入已更改的区域。
- the
framesPerSecond
to60
which seems to have helped. - Reduced the overall height of the printable area
- Calculated the area of change and simple called
repaint(Rectangle)
passing in only that area which has changed.
另一个问题是你的动画在很短的时间内有很大的覆盖范围。增加时间也会使它更平滑(或减小宽度和/或高度)
The other problem is your animation has a large area to cover in a very short period of time. Increasing the amount of time will also make it smoother (or reducing the width and/or height)
import java.awt.Color;
import java.awt.Dimension;
import java.awt.EventQueue;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.Rectangle;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.Timer;
import javax.swing.UIManager;
import javax.swing.UnsupportedLookAndFeelException;
public class ScratchSpace {
public static void main(String[] args) {
EventQueue.invokeLater(new Runnable() {
@Override
public void run() {
try {
UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName());
} catch (ClassNotFoundException | InstantiationException | IllegalAccessException | UnsupportedLookAndFeelException ex) {
}
AnimatedPanel panel = new AnimatedPanel();
JFrame frame = new JFrame();
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.setContentPane(panel);
frame.pack();
frame.setLocationRelativeTo(null);
frame.setVisible(true);
panel.animate();
}
});
}
public static class AnimatedPanel extends JPanel {
private float progress = 0.0f; // a number between 0.0 and 1.0
public AnimatedPanel() {
setPreferredSize(new Dimension(800, 100));
}
public void animate() {
final int animationTime = 1000;
int framesPerSecond = 60;
int delay = 1000 / framesPerSecond;
final long start = System.currentTimeMillis();
final Timer timer = new Timer(delay, null);
timer.addActionListener(new ActionListener() {
@Override
public void actionPerformed(ActionEvent e) {
final long now = System.currentTimeMillis();
final long elapsed = now - start;
int width = getWidth();
int height = getHeight();
int oldWidth = (int) (width * progress);
progress = (float) elapsed / animationTime;
int newWidth = (int) (width * progress);
repaint(new Rectangle(oldWidth, 0, newWidth - oldWidth, height));
if (elapsed >= animationTime) {
timer.stop();
}
}
});
timer.start();
}
@Override
protected void paintComponent(Graphics g) {
Graphics2D g2d = (Graphics2D) g;
int width = getWidth();
int progressWidth = (int) (width * progress);
g2d.setColor(Color.BLUE);
g2d.fillRect(0, 0, progressWidth, getHeight());
g2d.setColor(Color.RED);
g2d.fillRect(progressWidth, 0, width - progressWidth, getHeight());
}
}
}
另外,你可以生成你自己的后台缓冲区并更新它...
Alternativly, you could generate you own backing buffer and update it...
import java.awt.Color;
import java.awt.Dimension;
import java.awt.EventQueue;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.RenderingHints;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.geom.Rectangle2D;
import java.awt.image.BufferedImage;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.Timer;
import javax.swing.UIManager;
import javax.swing.UnsupportedLookAndFeelException;
public class ScratchSpace {
public static void main(String[] args) {
EventQueue.invokeLater(new Runnable() {
@Override
public void run() {
try {
UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName());
} catch (ClassNotFoundException | InstantiationException | IllegalAccessException | UnsupportedLookAndFeelException ex) {
}
AnimatedPanel panel = new AnimatedPanel();
JFrame frame = new JFrame();
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.setContentPane(panel);
frame.pack();
frame.setLocationRelativeTo(null);
frame.setVisible(true);
panel.animate();
}
});
}
public static class AnimatedPanel extends JPanel {
private float progress = 0.0f; // a number between 0.0 and 1.0
private BufferedImage buffer;
public AnimatedPanel() {
setPreferredSize(new Dimension(800, 600));
// setOpaque(true);
}
public void animate() {
final int animationTime = 1000;
int framesPerSecond = 60;
int delay = 1000 / framesPerSecond;
final long start = System.currentTimeMillis();
final Timer timer = new Timer(delay, null);
timer.addActionListener(new ActionListener() {
@Override
public void actionPerformed(ActionEvent e) {
final long now = System.currentTimeMillis();
final long elapsed = now - start;
int width = getWidth();
progress = (float) elapsed / animationTime;
updateBuffer();
repaint();
if (elapsed >= animationTime) {
timer.stop();
}
}
});
timer.start();
}
@Override
public void invalidate() {
buffer = null;
updateBuffer();
super.invalidate();
}
protected void updateBuffer() {
if (getWidth() > 0 && getHeight() > 0) {
if (buffer == null) {
buffer = new BufferedImage(getWidth(), getHeight(), BufferedImage.TYPE_INT_RGB);
}
Graphics2D g2d = buffer.createGraphics();
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);
int width = getWidth();
int height = getHeight();
float progressWidth = width * progress;
g2d.setColor(Color.BLUE);
g2d.fill(new Rectangle2D.Float(0, 0, progressWidth, height));
g2d.setColor(Color.RED);
g2d.fill(new Rectangle2D.Float(progressWidth, 0, width - progressWidth, height));
g2d.dispose();
}
}
@Override
protected void paintComponent(Graphics g) {
Graphics2D g2d = (Graphics2D) g;
if (buffer != null) {
g2d.drawImage(buffer, 0, 0, this);
}
// int width = getWidth();
// int progressWidth = (int) (width * progress);
// g2d.setColor(Color.BLUE);
// g2d.fillRect(0, 0, progressWidth, getHeight());
// g2d.setColor(Color.RED);
// g2d.fillRect(progressWidth, 0, width - progressWidth, getHeight());
}
}
}
你遇到的主要问题是基本上你要绘制的区域在你想要绘制它的时候会很大。
The major problem you have is basically the area you are trying to paint is to large for the time you want to paint it.
另一种选择
您可以创建一个 BufferedImage
,它代表进度条并随着进度更新而移动。此更新创建了一个兼容的缓冲图像,这将使渲染更快。
You could create a BufferedImage
that represents the progress bar and move as the progress updates. This update creates a compatible buffered image, which should make it faster to renderer
import java.awt.Color;
import java.awt.Dimension;
import java.awt.EventQueue;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.GraphicsConfiguration;
import java.awt.GraphicsEnvironment;
import java.awt.RenderingHints;
import java.awt.Transparency;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.geom.Rectangle2D;
import java.awt.image.BufferedImage;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.Timer;
import javax.swing.UIManager;
import javax.swing.UnsupportedLookAndFeelException;
public class ScratchSpace {
public static void main(String[] args) {
EventQueue.invokeLater(new Runnable() {
@Override
public void run() {
try {
UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName());
} catch (ClassNotFoundException | InstantiationException | IllegalAccessException | UnsupportedLookAndFeelException ex) {
}
AnimatedPanel panel = new AnimatedPanel();
JFrame frame = new JFrame();
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.setContentPane(panel);
frame.pack();
frame.setLocationRelativeTo(null);
frame.setVisible(true);
panel.animate();
}
});
}
public static class AnimatedPanel extends JPanel {
private float progress = 0.0f; // a number between 0.0 and 1.0
private BufferedImage buffer;
public AnimatedPanel() {
setPreferredSize(new Dimension(800, 600));
// setOpaque(true);
}
public void animate() {
final int animationTime = 1000;
int framesPerSecond = 60;
int delay = 1000 / framesPerSecond;
final long start = System.currentTimeMillis();
final Timer timer = new Timer(delay, null);
timer.addActionListener(new ActionListener() {
@Override
public void actionPerformed(ActionEvent e) {
final long now = System.currentTimeMillis();
final long elapsed = now - start;
progress = (float) elapsed / animationTime;
repaint();
if (elapsed >= animationTime) {
timer.stop();
}
}
});
timer.start();
}
@Override
public void invalidate() {
buffer = null;
updateBuffer();
super.invalidate();
}
protected void updateBuffer() {
if (getWidth() > 0 && getHeight() > 0) {
if (buffer == null) {
GraphicsConfiguration config = GraphicsEnvironment.getLocalGraphicsEnvironment().getDefaultScreenDevice().getDefaultConfiguration();
buffer = config.createCompatibleImage(getWidth(), getHeight(), Transparency.TRANSLUCENT);
buffer.coerceData(true);
Graphics2D g2d = buffer.createGraphics();
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);
int width = getWidth();
int height = getHeight();
g2d.setColor(Color.BLUE);
g2d.fill(new Rectangle2D.Float(0, 0, width, height));
g2d.dispose();
}
}
}
@Override
protected void paintComponent(Graphics g) {
Graphics2D g2d = (Graphics2D) g;
int width = getWidth();
int progressWidth = (int) (width * progress);
int x = (progressWidth - width);
System.out.println(progressWidth + "; " + x);
// g2d.setColor(Color.BLUE);
// g2d.fillRect(0, 0, progressWidth, getHeight());
g2d.setColor(Color.RED);
g2d.fillRect(progressWidth, 0, width - progressWidth, getHeight());
g2d.drawImage(buffer, x, 0, this);
}
}
}
这篇关于如何使Java Swing动画更流畅的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持!