我正在制作一个简单的动画处理。我想动画图像从其起点到屏幕上定义的x,y值。
我有两个方法,update()
和Draw()
,它们在每个刻度上运行。update()
是代码将在下一个刻度处理x/y坐标以提供给Draw()
方法的地方。
draw()
方法然后绘制图像,传入更新的x和y值。
最小示例:
class ScrollingNote {
float x;
float y;
float destX;
float destY;
PImage noteImg;
ScrollingNote(){
noteImg = loadImage("image-name.png");
this.x = width/2;
this.y = 100;
this.destX = 100;
this.destY = height;
}
void update(){
// TODO: adjust this.x and this.y
// to draw the image slightly closer to
// this.destX and this.destY on the redraw
// ie in this example we are animating from x,y to destX, destY
}
void draw(){
image( noteImg, this.x, this.y );
}
}
我需要进行什么样的计算来调整x/y坐标以使图像稍微靠近目标?
这是一个非常基本的例子基于时间段的动画
它将在5秒内为可动画
对象设置动画。如果您想变得花哨,您可以简单地使用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
并再次运行它。
我也更喜欢使用动画库,因为它们添加了额外的功能,如插值,允许在不改变持续时间的情况下在某些时间点改变动画的速度,这将允许您执行诸如慢入,慢出(斜坡/出)效果之类的操作,使动画更具吸引力。
看一下…
如果您使用的是处理2.0,这可以通过Ani库来完成。要获得与@MadProgrammer相同的输出,您只需使用Ani. init(this)
设置基本草图,然后在绘制()
函数中通过翻译()
移动框,并通过rotate()
函数旋转它。整个动画在第一次单击鼠标后开始。
import de.looksgood.ani.*;
import de.looksgood.ani.easing.*;
float posX = 25, posY = 25;
float angleRotation = 0;
void setup () {
size (200, 200);
background (99);
noFill ();
stroke (0);
Ani.init(this);
frameRate (30);
rectMode(CENTER);
}
void draw () {
background (225);
translate(posX, posY);
rotate(radians(angleRotation));
rect(0, 0, 50, 50);
}
void mousePressed() {
Ani.to(this, 5, "posX", 175, Ani.LINEAR);
Ani.to(this, 5, "posY", 175, Ani.LINEAR);
Ani.to(this, 5, "angleRotation", 360, Ani.LINEAR);
}
手动地,您可以通过在绘图循环中增加posX
、posY
和angleRotation
来获得类似的结果。