12.15 Advanced Animation

Way back in Example 12-4, we saw a simple animation technique that suffered, unfortunately, from flickering. Example 12-19 is a program that performs a more graphics-intensive animation but doesn't flicker, because it uses a technique known as double-buffering : it draws each frame of the animation off-screen, then copies the frame onto the screen all at once. This example also has better performance because it requests redraws of only the relatively small portion of the screen that needs to be redrawn.

Another interesting feature of this example is its use of the javax.swing.Timer class to call the actionPerformed( ) method of a specified ActionListener object at specified intervals. The Timer class is used here so that you don't have to create a Thread. (Note that Java 1.3 includes java.util.Timer, a class that is similar to, but quite distinct from, javax.swing.Timer.)

Example 12-19. Hypnosis.java
package je3.graphics; import java.awt.*; import java.awt.event.*; import java.awt.image.*; import javax.swing.*; import javax.swing.Timer;  // Import explicitly because of java.util.Timer /**  * A Swing component that smoothly animates a spiral in a hypnotic way.  **/ public class Hypnosis extends JComponent implements ActionListener {     double x, y;           // The center of the spiral     double r1, r2;         // The inner and outer radii of the spiral     double a1, a2;         // The start and end angles of the spiral     double deltaA;         // How much the angle changes in each frame     double deltaX, deltaY; // The trajectory of the center     float linewidth;       // How wide the lines are     Timer timer;           // The object that triggers the animation     BufferedImage buffer;  // The image we use for double-buffering     Graphics2D osg;        // Graphics2D object for drawing into the buffer     public Hypnosis(double x, double y, double r1, double r2,                     double a1, double a2, float linewidth, int delay,                     double deltaA, double deltaX, double deltaY)     {         this.x = x; this.y = y;         this.r1 = r1; this.r2 = r2;         this.a1 = a1; this.a2 = a2;         this.linewidth = linewidth;         this.deltaA = deltaA;         this.deltaX = deltaX;         this.deltaY = deltaY;         // Set up a timer to call actionPerformed( ) every delay milliseconds         timer = new Timer(delay, this);         // Create a buffer for double-buffering         buffer = new BufferedImage((int)(2*r2+linewidth),                                    (int)(2*r2+linewidth),                                     BufferedImage.TYPE_INT_RGB);         // Create a Graphics object for the buffer, and set the linewidth         // and request antialiasing when drawing with it         osg = buffer.createGraphics( );         osg.setStroke(new BasicStroke(linewidth, BasicStroke.CAP_ROUND,                                       BasicStroke.JOIN_ROUND));         osg.setRenderingHint(RenderingHints.KEY_ANTIALIASING,                              RenderingHints.VALUE_ANTIALIAS_ON);     }     // Start and stop the animation by starting and stopping the timer     public void start( ) { timer.start( ); }     public void stop( ) { timer.stop( ); }     /**       * Swing calls this method to ask the component to redraw itself.      * This method uses double-buffering to make the animation smoother.      * Swing does double-buffering automatically, so this may not actually      * make much difference, but it is important to understand the technique.      **/     public void paintComponent(Graphics g) {         // Clear the background of the off-screen image         osg.setColor(getBackground( ));         osg.fillRect(0, 0, buffer.getWidth( ), buffer.getHeight( ));         // Now draw a black spiral into the off-screen image         osg.setColor(Color.black);         osg.draw(new Spiral(r2+linewidth/2, r2+linewidth/2, r1, a1, r2, a2));         // Now copy that off-screen image onto the screen         g.drawImage(buffer, (int)(x-r2), (int)(y-r2), this);     }     /**       * This method implements the ActionListener interface.  Our Timer object      * calls this method periodically.  It updates the position and angles      * of the spiral and requests a redraw.  Instead of redrawing the entire      * component, however, this method requests a redraw only for the       * area that has changed.      **/     public void actionPerformed(ActionEvent e) {         // Ask to have the old bounding box of the spiral redrawn.         // Nothing else has anything drawn in it, so it doesn't need a redraw         repaint((int)(x-r2-linewidth), (int)(y-r2-linewidth),                 (int)(2*(r2+linewidth)), (int)(2*(r2+linewidth)));         // Now animate: update the position and angles of the spiral         // Bounce if we've hit an edge         Rectangle bounds = getBounds( );         if ((x - r2 + deltaX < 0) || (x + r2 + deltaX > bounds.width))             deltaX = -deltaX;         if ((y - r2 + deltaY < 0) || (y + r2 + deltaY > bounds.height))             deltaY = -deltaY;         // Move the center of the spiral         x += deltaX;         y += deltaY;         // Increment the start and end angles;         a1 += deltaA;         a2 += deltaA;         if (a1 > 2*Math.PI) {  // Don't let them get too big             a1 -= 2*Math.PI;             a2 -= 2*Math.PI;         }         // Now ask to have the new bounding box of the spiral redrawn.  This         // rectangle will be intersected with the redraw rectangle requested         // above, and only the combined region will be redrawn         repaint((int)(x-r2-linewidth), (int)(y-r2-linewidth),                 (int)(2*(r2+linewidth)), (int)(2*(r2+linewidth)));     }     /** Tell Swing not to double-buffer for us, since we do our own */     public boolean isDoubleBuffered( ) { return false; }     /** This is a main( ) method for testing the component */     public static void main(String[  ] args) {         JFrame f = new JFrame("Hypnosis");         Hypnosis h = new Hypnosis(200, 200, 10, 100, 0, 5*Math.PI, 10, 100,                                   2*Math.PI/15, 3, 5);         f.getContentPane( ).add(h, BorderLayout.CENTER);         f.setSize(400, 400);         f.show( );         h.start( );     } }


Java Examples in a Nutshell
Java Examples in a Nutshell, 3rd Edition
ISBN: 0596006209
EAN: 2147483647
Year: 2003
Pages: 285

flylib.com © 2008-2017.
If you may any questions please contact us: flylib@qtcs.net