这是一个非常基本的基于时间段的动画示例
它将在 5 秒内为 Animatable 对象设置动画。如果您想变得花哨,可以简单地使用 List 并同时更新/绘制多个对象。
import java.awt.BorderLayout;
import java.awt.Dimension;
import java.awt.EventQueue;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.Point;
import java.awt.Rectangle;
import java.awt.RenderingHints;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.geom.AffineTransform;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.Timer;
import javax.swing.UIManager;
import javax.swing.UnsupportedLookAndFeelException;
public class AnimationTest {
public static void main(String[] args) {
new AnimationTest();
}
public AnimationTest() {
EventQueue.invokeLater(new Runnable() {
@Override
public void run() {
try {
UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName());
} catch (ClassNotFoundException | InstantiationException | IllegalAccessException | UnsupportedLookAndFeelException ex) {
}
JFrame frame = new JFrame("Testing");
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.setLayout(new BorderLayout());
frame.add(new TestPane());
frame.pack();
frame.setLocationRelativeTo(null);
frame.setVisible(true);
}
});
}
public interface Animatable {
public void update(double progress);
public void draw(Graphics2D g2d);
}
public static class FlyingSquiral implements Animatable {
private final Point startPoint;
private final Point targetPoint;
private final double startAngel;
private final double targetAngel;
private Point location;
private double angle;
public FlyingSquiral(Point startPoint, Point targetPoint, double startAngel, double targetAngel) {
this.startPoint = startPoint;
this.targetPoint = targetPoint;
this.startAngel = startAngel;
this.targetAngel = targetAngel;
location = new Point(startPoint);
angle = startAngel;
}
@Override
public void update(double progress) {
location.x = (int)Math.round(startPoint.x + ((targetPoint.x - startPoint.x) * progress));
location.y = (int)Math.round(startPoint.y + ((targetPoint.y - startPoint.y) * progress));
angle = startAngel + ((targetAngel - startAngel) * progress);
}
@Override
public void draw(Graphics2D g2d) {
Graphics2D clone = (Graphics2D) g2d.create();
clone.setRenderingHint(RenderingHints.KEY_ALPHA_INTERPOLATION, RenderingHints.VALUE_ALPHA_INTERPOLATION_QUALITY);
clone.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);
clone.setRenderingHint(RenderingHints.KEY_COLOR_RENDERING, RenderingHints.VALUE_COLOR_RENDER_QUALITY);
clone.setRenderingHint(RenderingHints.KEY_DITHERING, RenderingHints.VALUE_DITHER_ENABLE);
clone.setRenderingHint(RenderingHints.KEY_FRACTIONALMETRICS, RenderingHints.VALUE_FRACTIONALMETRICS_ON);
clone.setRenderingHint(RenderingHints.KEY_INTERPOLATION, RenderingHints.VALUE_INTERPOLATION_BILINEAR);
clone.setRenderingHint(RenderingHints.KEY_RENDERING, RenderingHints.VALUE_RENDER_QUALITY);
clone.setRenderingHint(RenderingHints.KEY_STROKE_CONTROL, RenderingHints.VALUE_STROKE_PURE);
AffineTransform at = new AffineTransform();
at.translate(location.x, location.y);
at.rotate(Math.toRadians(angle), 25, 25);
clone.setTransform(at);
clone.draw(new Rectangle(0, 0, 50, 50));
clone.dispose();
}
}
public static class TestPane extends JPanel {
public static final long DURATION = 5000;
private long startTime;
private boolean started = false;
private FlyingSquiral squiral;
public TestPane() {
squiral = new FlyingSquiral(
new Point(0, 0),
new Point(150, 150),
0d, 360d);
Timer timer = new Timer(40, new ActionListener() {
@Override
public void actionPerformed(ActionEvent e) {
if (!started) {
startTime = System.currentTimeMillis();
started = true;
}
long time = System.currentTimeMillis();
long duration = time - startTime;
if (duration > DURATION) {
duration = DURATION;
((Timer)e.getSource()).stop();
}
double progress = (double)duration / (double)DURATION;
squiral.update(progress);
repaint();
}
});
timer.start();
}
@Override
public Dimension getPreferredSize() {
return new Dimension(200, 200);
}
@Override
protected void paintComponent(Graphics g) {
super.paintComponent(g);
Graphics2D g2d = (Graphics2D) g.create();
squiral.draw(g2d);
g2d.dispose();
}
}
}
同样,您可以使用基于约束的动画,其中对象不断移动,直到满足所需的约束(角度/位置)。各有利弊。
我更喜欢基于时间段的方法,因为它允许我应用不同的转换,而无需关心预先计算增量。试试这个,把目标天使从360改成720再运行一次。
我也更喜欢使用动画库,因为它们添加了额外的功能,比如插值,允许在不改变持续时间的情况下改变动画在特定时间点的速度,这可以让你做一些事情,比如慢进,减慢(加速/减速)效果,使动画更具吸引力。
看看……