Welcome to OGeek Q&A Community for programmer and developer-Open, Learning and Share
Welcome To Ask or Share your Answers For Others

Categories

0 votes
448 views
in Technique[技术] by (71.8m points)

animation - How to animate from one x,y coordinate to another? (Java/Processing)

I am making a simple animation in Processing. I want to animate an image from its starting point to a defined x,y value on the screen.

I have 2 methods, update() and draw(), which are run on every tick. update() is where the code will go to process the x/y coordinates to provide to the draw() method on the next tick.

The draw() method then draws the image, passing in the updated x and y values.

minimal example:

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 );
  }
}

What sort of calculation do I need to make to adjust the x/y coordinates to make the image draw slightly closer to the destination?

See Question&Answers more detail:os

与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
Welcome To Ask or Share your Answers For Others

1 Reply

0 votes
by (71.8m points)

This is a very basic example of time period based animation

It will animate a Animatable object over a 5 second period. You could simply use a List and update/paint multiple objects simultaneously if you wanted to get fancy.

enter image description here

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();
        }
    }

}

Equally, you could use a constraint based animation, where by the object keeps moving until it meets it's required constraints (angel/position). Each has pros and cons.

I prefer a time period based approach as it allows me to apply different transformations without needing to care about pre-calculating the delta. Try this, change the target angel from 360 to 720 and run it again.

I also prefer to use an animation library, as they add additional features, like interpolation, allowing to change the speed of the animation at certain points in time without changing the duration, this would allow you to do things like slow in, slow out (ramp up/out) effects, making the animation more appealing.

Take a look at...


与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
OGeek|极客中国-欢迎来到极客的世界,一个免费开放的程序员编程交流平台!开放,进步,分享!让技术改变生活,让极客改变未来! Welcome to OGeek Q&A Community for programmer and developer-Open, Learning and Share
Click Here to Ask a Question

...