|
Część 4.3. Animacja
Poznałeś już metody rysowania. Kolejnym krokiem będzie wprowadzenie do apletu elementów animacji. Zaczniemy od napisania apletu wyświetlającego zegar analogowy wyświetlający aktualny czas systemowy. Zastosujemy najprostszą metodę animacji: zmazywanie elementu przez wyświetlenie go w kolorze tła, a następnie wyrysowanie go w nowej pozycji. Oto kod tego apletu:
import java.util.*;
import java.awt.*;
import java.applet.*;
public class ZegarAnalogowy extends Applet implements Runnable
{ int lastxs=0, lastys=0, lastxm=0, lastym=0, lastxh=0, lastyh=0;
public void start()
{ while (true)
{ repaint();
for (int i=0 ; i<=10000 ; i++) { }
}
}
// główna część apletu rysująca zegar
public void paint(Graphics g)
{ int xh, yh, xm, ym, xs, ys, s, m, h;
Date dat = new Date();
// pobranie aktualnego czasu systemowego
s = dat.getSeconds();
m = dat.getMinutes();
h = dat.getHours();
// rysowanie tła apletu
g.setColor(new Color(255,255,221));
g.fillRect(0, 0, 210, 210);
g.setColor(new Color(0,0,153));
// wyliczenie współrzędnych końca każdej wskazówki zegara
// (początek jest zawsze w środku zegara)
xs = (int)(Math.cos(s * 3.14f/30 - 3.14f/2) * 97 + 105);
ys = (int)(Math.sin(s * 3.14f/30 - 3.14f/2) * 97 + 105);
xm = (int)(Math.cos(m * 3.14f/30 - 3.14f/2) * 90 + 105);
ym = (int)(Math.sin(m * 3.14f/30 - 3.14f/2) * 90 + 105);
xh = (int)(Math.cos((h*30 + m/2) * 3.14f/180 - 3.14f/2) * 75 + 105);
yh = (int)(Math.sin((h*30 + m/2) * 3.14f/180 - 3.14f/2) * 75 + 105);
// rysowanie tarczy zegara i cyfr
g.setFont(new Font("Arial", Font.BOLD, 16));
g.setColor(Color.blue);
g.drawOval(5, 5, 200, 200);
g.setColor(Color.white);
g.fillOval(7, 7, 196, 196);
g.setColor(Color.darkGray);
g.drawString("9", 200, 108);
g.drawString("3", 194, 108);
g.drawString("12",96, 25);
g.drawString("6", 102, 203);
// "ścieranie" wskazówek, jeżeli zmieniły się współrzędne
g.setColor(Color.white);
if (xs != lastxs || ys != lastys)
{ g.drawLine(105, 105, lastxs, lastys);
}
if (xm != lastxm || ym != lastym)
{ g.drawLine(105, 104, lastxm, lastym);
g.drawLine(104, 105, lastxm, lastym);
}
if (xh != lastxh || yh != lastyh)
{ g.drawLine(105, 104, lastxh, lastyh);
g.drawLine(104, 105, lastxh, lastyh);
}
// rysowanie wskazówek zegara
g.setColor(Color.red);
g.drawLine(105, 105, xs, ys);
g.setColor(Color.blue);
g.drawLine(105, 104, xm, ym);
g.drawLine(104, 105, xm, ym);
g.drawLine(105, 104, xh, yh);
g.drawLine(104, 105, xh, yh);
// zapamiętanie aktualnych współrzędnych wskazówek
lastxs=xs;
lastys=ys;
lastxm=xm;
lastym=ym;
lastxh=xh;
lastyh=yh;
}
}
Przeanalizujmy działanie apletu krok po kroku:
Spróbuj teraz samodzielnie napisać aplet, który będzie wyświetlał koło, kwadrat i trójkąt
w losowo wybranych miejscach. Figury powinny pojawiać siś i znikać w różnych miejscach apletu, ale
zawsze ma być widoczna tylko jedna z nich
Kolejny nasz aplet będzie wyświetlał napis pływający wewnątrz utworzonego okna i "odbijał" się
od krawędzi. Zastosujemy na początek poznaną już z poprzedniego przykładu metodę animacji: zmazywanie
aktualnie wyświetlanego tekstu i wyrysowanie go w nowej pozycji. Oto kod tego apletu:
import java.awt.*;
import java.applet.*;
public class AniTekst1 extends Applet
{ int fontSize = 10, x=0, y=299, dx=1, dy=-1, wys, szer;
// zmienna do określenia czy aplet ma być przerysowany, czy nie
boolean zatrzymaj = true;
String napis = new String ("TEKST");
Font font = new Font("Arial", Font.BOLD, 24);
FontMetrics fm;
// każde wejście na stronę (do okna) uruchamia odrysowywanie apletu
public void start ()
{ zatrzymaj = false;
repaint();
}
// każde opuszczenie strony (okna) zatrzymuje odrysowywanie apletu
public void stop ()
{ zatrzymaj = true;
}
// rysowanie zawartości apletu
public void paint(Graphics g)
{ // możesz w warunku pętli while zamienić zmienna zatrzymaj na stałą true
// otrzymany efekt będzie raczej mało zadawalający: odrysowywanie "oszaleje"
while (! zatrzymaj)
{ g.setColor (Color.white); // rysowanie białego tła apletu
g.fillRect (0, 0, 299, 299);
// zamazywanie istniejącego napisu
g.setFont (font);
g.drawString (napis, 5, 75);
// rysowanie ramki wokół apletu
g.setColor (Color.red);
g.drawRect (0, 0, 299, 299);
// pobranie wysokości i szerokości wyświetlanego tekstu
fm = g.getFontMetrics();
wys = fm.getHeight();
szer = fm.stringWidth(napis);
// zmiana współrzędnych i ustalenie nowej pozycji tekstu
if (x <= 0 && dx == -1) dx=1;
if (x + szer >= 299 && dx == 1) dx=-1;
if (y <= wys && dy == -1) dy=1;
if (y >= 299 && dy == 1) dy=-1;
x += dx;
y += dy;
// wyświetlenie tekstu
g.drawString (napis, x, y);
// pętla opóźniająca kolejne wyświetlanie
for (int i = 0; i < 100000; i++);
}
}
}
Aplet działa w zasadzie poprawnie. Ma jednak kilka wad:
import java.awt.*;
import java.applet.*;
public class AniTekst2 extends Applet implements Runnable
{ int fontSize = 10, x = 0, y = 299, dx = 1, dy = -1, wys, szer;
String napis = new String ("TEKST");
Font font = new Font("Arial", Font.BOLD, 24);
FontMetrics fm;
Thread thread; // klasa obsługi wątków programu
Image obraz; // obiekt do przechowywania obrazu w pamięci
Graphics g1, g2; // obiekty graficzne
// inicjacje apletu wykonywana tylko raz podczas jego ładowania do pamięci
public void init()
{ setBackground(Color.white);
obraz = createImage (300, 300);
g2 = obraz.getGraphics();
g2.setFont (font);
g2.setColor (Color.red);
fm = g2.getFontMetrics();
wys = fm.getHeight();
szer = fm.stringWidth(napis);
g1 = getGraphics();
}
// utworzenie i uruchomienie wątku przy każdym uaktywnieniu apletu
public void start()
{ thread = new Thread (this);
thread.start();
}
// metoda, od której zaczyna się wykonanie uruchomionego wątku
public void run()
{ while (true)
{ if (x<=0 && dx==-1) dx=1;
if (x+szer>=299 && dx==1) dx=-1;
if (y<=wys && dy==-1) dy=1;
if (y>=299 && dy==1) dy=-1;
x += dx;
y += dy;
g2.clearRect(0, 0, 299, 299);
g2.drawRect (0, 0, 299, 299);
g2.drawString (napis, x, y);
g1.drawImage (obraz, 0, 0, this);
try
{ Thread.sleep(10); }
catch (InterruptedException e) { }
}
}
}
Przeanalizujmy teraz działanie tego apletu krok po kroku:
Napisz samodzielnie aplet, który będzie animował tekst przewijany tylko w poziomie. Tekst
powinien przewijać się z prawej strony w lewo i po całkowitym zniknięciu za lewą krawędzią pojawić się
ponownie z prawej strony.
Poprzedni aplet może stanowić efektowne uatrakcyjnienie strony WWW. Z tego punktu widzenia ma jednak
bardzo istotną wadę: jeżeli chcielibyśmy zmienić w nim tekst, kolory, czcionki, czy choćby sam rozmiar,
to musimy tworzyć kolejną wersję apletu i ponownie go kompilować, aby użyć na stronie. Jest to bardzo
niewygodne, a dodatkowo mnoży ilość tworzonych apletów wykonujących w zasadzie takie samo zadanie.
Istnieje metoda pozwalająca wykorzystać ten sam aplet w różnych sytuacjach: musimy nasz aplet sparametryzować. Oznacza to, że samo działanie będzie zawsze takie samo, ale podczas uruchamiania apletu możemy każdorazowo przekazywać mu inne informacje o tym co i w jaki sposób ma wyświetlać. Osiągnięcie tego wymaga:
Z użyciem parametrów wiążą się dwa problemu. Po pierwsze - użytkownik może w ogóle nie podać parametru. Uwzględniając tą sytuację należy wszystkim zmiennym korzystającym z parametrów nadać wartość domyślną, co zapobiegnie ewentualnym błędom. Po drugie - parametry mogą zawierać błędne wartości (np. tekst dla zmiennej numerycznej). Problem jest poważny i wymaga rozbudowanej kontroli. W naszym przykładzie go nie rozwiążemy ze względu na brak miejsca, ale pisząc aplet należy o nim pamiętać. Pora już przerobić nasz aplet tak, aby można było nim sterować ze strony www bez konieczności przerabiania samego apletu i jego ponownej kompilacji, gdy chcemy coś w nim zmienić (oczywiście poza samym działaniem). Oto kod tego apletu:
import java.awt.*;
import java.applet.*;
public class AniTekst4 extends Applet implements Runnable
{ int x, y, dx = 1, dy = -1, wys, szer;
FontMetrics fm;
Thread thread;
Image obraz;
Graphics g1, g2;
// zmienne do zapamiętania rozmiarów apletu:
int Height;
int Width;
// zmienne do przetworzenia parametrów:
String strParam; // zmienna robocza
String napis = "TEKST"; // wyświetlany tekst
Color BgColor = Color.white; // kolor tła
Color FgColor = Color.red; // kolor tekstu
Color BorderColor = Color.red; // kolor ramki
String FontFamily = "Arial"; // czcionka
int FontSize = 24; // wielkość czcionki w pikselach
int FontStyle = Font.PLAIN; // krój czcionki
Font font;
public void init()
{ /********** POCZˇTEK USTAWIANIA PARAMETRÓW **********/
// wyświetlany tekstu
if (this.getParameter("Text") != null)
{ napis = this.getParameter("Text"); }
// kolor tekstu
if (this.getParameter("FgColor") != null)
{ strParam = this.getParameter("FgColor");
FgColor = new Color(Integer.parseInt(strParam, 16));
}
// kolor tła apletu
if (this.getParameter("BgColor") != null)
{ strParam = this.getParameter("BgColor");
BgColor = new Color(Integer.parseInt(strParam, 16));
}
// kolor ramki apletu
if (this.getParameter("BorderColor") != null)
{ strParam = this.getParameter("BorderColor");
BorderColor = new Color(Integer.parseInt(strParam, 16));
}
// krój czcionki
if (this.getParameter("FontFamily") != null)
{ FontFamily = this.getParameter("FontFamily"); }
// wielkość czcionki
if (this.getParameter("FontSize") != null)
{ strParam = this.getParameter("FontSize");
FontSize = Integer.parseInt(strParam);
}
// styl czcionki
if (this.getParameter("FontStyle") != null)
{ strParam = this.getParameter("FontStyle");
strParam = strParam.toLowerCase();
if (strParam.equals("bold")) { FontStyle = Font.BOLD; }
if (strParam.equals("italic")) { FontStyle = Font.ITALIC; }
if (strParam.equals("plain")) { FontStyle = Font.PLAIN; }
}
font = new Font(FontFamily, FontStyle, FontSize);
/********** KONIEC USTAWIANIA PARAMETRÓW **********/
Height = getSize().height;
Width = getSize().width;
setBackground(BgColor);
obraz = createImage (Width, Height);
g2 = obraz.getGraphics();
g2.setFont (font);
fm = g2.getFontMetrics();
wys = fm.getHeight();
szer = fm.stringWidth(napis);
g1 = getGraphics();
x = 0;
y = Height-1;
}
public void start()
{ thread = new Thread (this);
thread.start();
}
// teraz metoda run(), ale całkowicie sparametryzowana
public void run()
{ while (true)
{ if (x<=0 && dx==-1) dx=1;
if (x+szer>=Width-1 && dx==1) dx=-1;
if (y<=wys && dy==-1) dy=1;
if (y>=Height-1 && dy==1) dy=-1;
x += dx;
y += dy;
g2.clearRect(0, 0, Width-1, Height-1);
g2.setColor (BorderColor);
g2.drawRect (0, 0, Width-1, Height-1);
g2.setColor (FgColor);
g2.drawString (napis, x, y);
try
{ Thread.sleep(10); }
catch (InterruptedException e){ }
g1.drawImage (obraz, 0, 0, this);
}
}
}
Spróbujmy teraz uruchomić nasz aplet zmieniając mu parametry. Poniżej masz przykładowy zapis uruchomienia tego apletu ze strony www. Możesz go uruchomić bez podawania parametrów lub podając własne parametry. Pamiętaj jednak, że błędne wartości np. kolorów lub rozmiarów apletu mogą dać w efekcie nieprzewidziany efekt. Zwróć przy tym uwagę, że wielkość liter w nazwie parametru nie ma znaczenia. Na zakończenie animacji aplet najbardziej chyba efektowny, choć dość prosty do napisania. Mając serię grafik (zdjęć, obrazków) możemy stworzyć animację przez wyświetlanie kolejnych obrazów (tzw. animacja poklatkowa). Wykonujemy to przez wczytanie do tablicy wszystkich obrazów, a następnie wyświetlanie ich w określonej kolejności. Poniższy aplet jest tego przykładem. Po przeanalizowaniu poprzednich przykładów nie powinieneś mieć problemów ze zrozumieniem działania tego aplet, dlatego też nie ma w nim żadnych komentarzy. Spróbuj przeanalizować go samodzielnie. Zwróć uwagę na metodę załadowania wszystkich obrazków przed rozpoczęciem wyświetlania animacji.
import java.applet.*;
import java.awt.*;
public class Animacja extends Applet implements Runnable
{ Graphics g1, g2;
Image imgArray[]=new Image[10];
Image img;
boolean start = true;
public void init()
{ g1 = getGraphics();
img = createImage(540, 240);
g2 = img.getGraphics();
for (int i = 0; i < 10; i++)
imgArray[i] = getImage(getDocumentBase(), "T" + (i+1) + ".gif");
setBackground(new Color(255, 255, 226));
}
public void start()
{ Thread thread = new Thread (this);
thread.start();
}
public void run()
{ int n, i = 0, j = 1;
Image imgR;
while (true)
{ if (start)
{ for (n = 0; n < 10; n++)
{ imgR = getImage(getDocumentBase(), "T" + (n+1) + ".gif");
imgArray[n] = imgR;
g2.setColor(Color.red);
g2.drawString("Ładowanie grafiki...", 200, 90);
g2.drawRect(140, 130, 260, 30);
g2.setColor(Color.blue);
g2.fillRect(141, 131, 26*n, 28);
g1.drawImage (img, 0, 0, this);
}
start = false;
}
else
{ g2.clearRect(0, 0, 540, 240);
g2.setColor(new Color(0, 0, 200));
for (n = 0 ; n < 4 ; n++)
g2.drawRect(n ,n, getSize().width-2*n-1, getSize().height-2*n-1);
g2.drawImage (imgArray[i], 40, 40, this);
g1.drawImage (img, 0, 0, this);
try { Thread.sleep(100); }
catch(InterruptedException e) { }
i += j;
if (i == 9) j = -1;
else if (i == 0) j = 1;
}
}
}
}
|