Une image qui bouge sur un dessin                  

Une image qui bouge sur un dessin

alt : l'applet n'est pas visible par votre navigateur ; pour voir l'application, vous devez télécharger le code, le compiler et l'exécuter

Lorsqu'une animation ne concerne qu'une partie d'une image, il peut y avoir un gain de temps important à ne pas redessiner toute l'image mais seulement la partie modifiée. On peut utiliser pour cela la méthode
  >  repaint(int x, int y, int largeur, int hauteur) qui repeint dans le composant le rectangle correspondant aux arguments. Il faut alors déterminer la position et les dimensions du rectangle à repeindre avant de l'indiquer à la méthode repaint.

Dans notre exemple, il s'agit de déplacer horizontalement une petite image au-dessus d'un dessin fixe. On ne retrace le contenu de la fenître que dans le rectangle contenant la partie qui a changé d'un temps de l'animation au suivant ; on calcule pour cela le rectangle à retracer. Le dessin est constitué d'un ensemble de demi-cercles de couleurs aléatoires. Lorsqu'on appuie sur le bouton couleur, le dessin est retracé avec de nouvelles couleurs. L'image de la Terre se déplace ou non selon l'action sur les boutons arret et reprendre.

import java.awt.BorderLayout;
import java.awt.Color;
import java.awt.Dimension;
import java.awt.Graphics;
import java.awt.Image;
import java.awt.Rectangle;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.io.File;
import java.io.IOException;
import java.util.Random;

import javax.imageio.ImageIO;
import javax.swing.JButton;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.Timer;


@SuppressWarnings("serial")
class Ardoise extends JPanel implements ActionListener {
	private Timer declencheur;
	private Random alea;
	private Image terre;
	private int largeur = 400, hauteur = 200;
	private int r, v, b;
	private int x; // abscisse du coin supérieur gauche de l'image
	private int largeurImage = 50; // largeur de l'image
	private int hauteurImage = 50; // hauteur de l'image
	private Rectangle vieuxRectangle;

	Ardoise() {
		setPreferredSize(new Dimension(largeur, hauteur));
		setBackground(Color.BLUE);
		alea = new Random();
		getCouleurs();
		x = - largeurImage - 5;
		try {
			terre = ImageIO.read(new File("world.gif"));
		}
		catch (IOException exc) {
			exc.printStackTrace();
		}
		x = - largeurImage - 5;
		vieuxRectangle = new Rectangle(0, 0, 0, 0); 
		declencheur = new Timer(100, this);
		declencheur.start();
	}

	void getCouleurs() {
		r = Math.abs(alea.nextInt()) % 256;
		v = Math.abs(alea.nextInt()) % 256;
		b = Math.abs(alea.nextInt()) % 256;
	}

	public void actionPerformed(ActionEvent e) {
		if (e.getSource() == declencheur) {
			x += 5;
			if (x > getWidth()) x = - largeurImage;

			/* on calcule l'union de l'ancien rectangle
			* contenant l'image et du nouveau rectangle */
			Rectangle nouveauRectangle = new Rectangle(x, getHeight()/3, largeurImage , hauteurImage);
			Rectangle rect = nouveauRectangle.union(vieuxRectangle);
			vieuxRectangle = nouveauRectangle;

			/* on repeint uniquement l'union des rectangles */
			repaint(rect.x, rect.y, rect.width, rect.height);
		}

		else {
			String commande = e.getActionCommand();
			if (commande.equals("arreter"))declencheur.stop();
			else if (commande.equals("reprendre")) declencheur.restart();
			else if (commande.equals("changerCouleurs")) {
				getCouleurs();
				repaint();
			}
		}
	}

	public void paintComponent(Graphics g) {
		super.paintComponent(g);
		largeur = getWidth();
		hauteur = getHeight();
		for (int i = 0 ; i < largeur; i++) {
			g.setColor(new Color((r + i / 3) % 255, (v + i) % 255, (b + i / 2 )% 255));
			g.drawArc(i, i, largeur - 2 * i, 2*hauteur - 2 * i, 0, 180);
		}
		if (terre != null) g.drawImage(terre, x, getHeight()/3,
 largeurImage, hauteurImage, this);
	}
}

@SuppressWarnings("serial")
class IHMMouvement extends JPanel implements ActionListener {
	private JButton arret = new JButton("arret");
	private JButton reprise = new JButton("reprendre");
	private JButton couleurs = new JButton("couleurs");
	private Ardoise ardoise = new Ardoise();

	IHMMouvement() {
		arret.setActionCommand("arreter");
		arret.addActionListener(ardoise);
		arret.addActionListener(this);

		reprise.setActionCommand("reprendre");
		reprise.addActionListener(ardoise);
		reprise.addActionListener(this);
		reprise.setEnabled(false);

		couleurs.setActionCommand("changerCouleurs");
		couleurs.addActionListener(ardoise);

		JPanel boutons = new JPanel();		
		boutons.setBackground(Color.BLUE);
		boutons.add(arret);
		boutons.add(reprise);
		boutons.add(couleurs);

		setLayout(new BorderLayout());
		add(boutons, BorderLayout.NORTH);
		add(ardoise, BorderLayout.CENTER);
	}

	public void actionPerformed(ActionEvent e) {
		if (e.getActionCommand().equals("arreter")) {
			arret.setEnabled(false);
			reprise.setEnabled(true);
		}

		else if (e.getActionCommand().equals("reprendre")) {
			arret.setEnabled(true);
			reprise.setEnabled(false);
		}
	}
}
public class Mouvement {
	public static void main(String[] arg) {
		JFrame fenetre = new JFrame();
		fenetre.setContentPane(new IHMMouvement());
		fenetre.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
		fenetre.pack();
		fenetre.setLocation(100, 100);
		fenetre.setVisible(true);
	}
}

nouveauRectangle.union(vieuxRectangle) : la méthode union de la classe java.awt.Rectangle renvoie le rectangle circonscrit à l'union du rectangle concerné et du rectangle indiqué en argument.
g.drawArc(i, i, largeur - 2*i, 2*hauteur - 2*i, 0, 180); : la méthode drawArc de la classe Graphics trace un arc d'ellipse ; les quatre premiers arguments indiquent le rectangle circonscrit agrave; l'ellipse ; le cinquième indique l'angle en degrés à partir duquel on trace (0 degré correspond à « 3 heures ») et le dernier donne le nombre de degrés sur lesquels on trace, comptés suivant le sens trigonométrique.
© Irène Charon, Télécom ParisTech 2011