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