Animacje

Adam Boduch

Tematem niniejszego artykułu będą proste animacje w Delphi. Zwykłe, napisy, ale efektowne. Nie jest to trudne, ale niezbędna na tym etapie będzie podstawowa wiedza na temat programowania grafiki i klasy TCanvas. W tym celu możesz poczytać odpowiedni rozdział kursu Delphi.

Podstawowe informacje

Przede wszystkim musisz wiedzieć, że taka animacja to nic innego jak pętla, która powoduje ciągłe rysowanie danego napisu, tyle, że za każdym razem w innym miejscu.

Na początek użytkownik musi mieć możliwość przerwania animacji w dowolnym momencie. W sekcji Interface stwórz dwie zmienne:

var
  DText : String; // tekst, który będzie wyświetlony na formie
  Done : Boolean; // jeżeli TRUE to animacja jest zakończona

Pierwsza z nich oznaczać będzie tekst, który będzie wyświetlany. Druga zmienna ma wartość True, jeżeli animacja została zakończona.

Do sekcji private dodaj dwie deklaracje procedur, które powodować będą wyświetlanie animacji, tyle, że dwóch różnych.

  private
    procedure DrawAnim(X, Y : Integer);
    procedure SteepBySteep(X, Y : Integer);

Zajmijmy się pierwszą z procedur. Zawiera dwa parametry - X i Y. Wywołując procedurę musimy podać dwa parametry położenia początkowego napisu.

Piszemy potrzebną procedurę...

Najpierw zaprezentuje Ci pierwszą z procedur. Ma ona za zadanie przesuwanie tekstu z jednego krańca do drugiego. Tekst będzie 3D.

procedure TMainForm.DrawAnim(X, Y: Integer);
var
  B : TBitmap; // Bitmapa
  i : Integer;
begin
  B := TBitmap.Create; // utwórz bitmapę
  B.Height := 40;  // wysokość bitmapy
  B.Width := Canvas.TextWidth(DText) * 10; // szerokość bitmapy
  B.Canvas.Font.Name := 'Courier New';  // czcionka
  B.Canvas.Font.Size := 14; // rozmiar czcionki
  B.Canvas.Brush.Color := Color; // kolor tła
// stwórz obszar na którym bitmapa ma być wyświetlana
  B.Canvas.FillRect(Rect(0, 0, Canvas.TextWidth(DText) * 10, 40));

  while not (Done) do // Jeżeli Done=False...
  begin // zacznij odtwarzanie animacji
    for i:= X to Width  do  // zacznij od punktu X to końca formy
    begin
      Application.ProcessMessages; // daj odetchnąć systemowi
      Sleep(10); // czekaj 1 milisekundę
      B.Canvas.Font.Color := clWhite; // kolor na biały
      B.Canvas.TextOut(10, 10, DText); // wyświetlenie tekstu
      B.Canvas.Brush.Style := bsClear; // tło na przezroczyste
      B.Canvas.Font.Color := clBlack; // kolor czcionki: czarny
      B.Canvas.TextOut(9, 9, DText); // wyświetl tekst jeszcze raz
      Canvas.Draw(I, Y, B); // wyświetl bitmapę
      if (Done) then // Jeżeli ktoś przerwał animacje ( Done=True )...
        Break; // ... to przerwij odtwarzanie animacji
    end;
  end;
  B.Free; // zwolnij pamięć dla zmiennej
end;

Nie przerażaj się wielkością kodu tej procedury. Wszystko wyjaśnię po kolei. Animacja nie jest wyświetlana bezpośrednio na Canvasie, ale na bitmapie, która jest tworzona podczas wykonywania programu. Dlaczego? Gdyż dzięki temu nie dostrzeżesz niewygodnego efektu migania.

Najważniejsza część znajduje się na początku. Następuje tu bowiem stworzenie bitmapy oraz wyznaczenie jej szerokości oraz wysokości i koloru tła, czcionki. Później pętla - jedna zagnieżdżona w drugiej. Pętla while ma za zadanie sprawdzać, czy zmienna Done ma wartość True, czy False. Jeżeli FALSE ( kontynuuj animacje ) wykonywana jest pętla for, która ma z kolei za zadanie przesuwanie tekstu. Przesuwanie będzie następowało od wartości, która jest zadeklarowana pod zmienną X do samego końca formy. Zwróć uwagę na bardzo ważne polecenie, które jest w pętli - Application.ProcessMessages. Powoduje ono, że system się nie "zawisza" i można wykonywać inne czynności. Kolejne określa odstęp czasu jaki upłynie między przesunięciem się napisu o jeden punkt. Później następuje narysowanie tekstu czcionką białą, a następnie zmiana tej czcionki na czarną i PONOWNE narysowanie tego samego tekstu tyle, że przesuniętego o jeden punkt - daje to efekt cienia. No i na końcu narysowanie bitmapy na formie ( polecenie Draw ). Pozostaje jeszcze sprawdzenie, czy zmienna Done ma wartość True ( jeżeli tak to animacja zostaje przerwana poleceniem Break ).

Jeżeli chcesz, aby animacja została przerwana w dowolnym miejscu programu umieszczasz kod:

Done := TRUE;

Literka po literce

Jeszcze jeden przykład animacji - maszyna do pisania - jedna literka po drugiej pojawiają się na ekranie dając efekt pisanego tekstu. Będzie to prostszy przykład. Polega on na rozdzieleniu liter w danym wyrazie i rysowaniu jednej do drugiej. Jest to trochę skomplikowane z tego względu iż w czcionkach każda litera ma różną szerokość w pikselach. Rozwiązaniem jest zastosowanie czcionki Courier New, której litery mają taką samą szerokość.

procedure TMainForm.SteepBySteep(X, Y: Integer);
var
  TextLength, I: Integer;
begin
  Done := TRUE; // skończ poprzednia animacje
  TextLength := Length(DText); // pobierz długość tekstu

  with Canvas do
  begin
    for I := 1 to TextLength do // pętelka...
    begin
      Application.ProcessMessages;
      Sleep(100);  // czekają 100 milisekund
      Brush.Style := bsClear; // styl na przezroczysty
      Font.Name := 'Courier New'; // czcionka
      Font.Color := clWhite; // kolor czcionki - biały
      Font.Size := 16;  // rozmiar
{ Wyświetlaj tekst jedna litera po drugiej z przesunięciem }
      TextOut((X + i * 16), Y, DText[i]);
      Brush.Style := bsClear;
      Font.Color :=  clBlack;
// wyswietl ten sam tekst w innym położeniu - efekt cienia
      TextOut((X + i * 16) -2, Y -2, DText[i]); 
   end;
  end;
end;

Na samym początku tej procedury pobierana jest ( do zmiennej TextLength ) długość tekstu w znakach. Później pętla wykonywana jest tyle razy ile znaków ma dany wyraz. Następnie polecenia, które już znasz, czyli przypisanie odpowiedniej czcionki oraz koloru i kroju. Później najtrudniejsza część zadania, czyli samo narysowanie litery. Należy określić wartość X ( w pierwszym parametrze ). Wszystko jest mnożone przez 16 i to daje odstęp i w rezultacie narysowanie litery. Później zmiana koloru czcionki i narysowanie litery jeszcze raz, tyle, że z przesunięciem co znowu daje efekt cienia.

To tylko proste animacje - kolejny krok to wykorzystanie OpenGL'a. Kurs taki możesz znaleźć w dziale Kursy.

Animacje tekstowe

Zanim skończę jeszcze trochę o animacjach. Tym razem tekstowa - maszyna do pisania. Tekst, litera po literze przelatujący na pasku aplikacji. Zrobimy tak, że na pasku linia po linii będzie mógł zostać wyświetlony cały tekst. Taką linię umieść w sekcji private:

    procedure Go(const PText : TStrings);

Zauważ, że posiada ona parametr, który jest typu TStrings. A ten typ możesz przechowywać tekst z całych plików tekstowych (linia po linii). Zanim zaczniemy trzeba jeszcze dodać jedną zmienną w sekcji Implementation:

var Running : Boolean=TRUE;  // zmienna okresla, czy animacja ma byc uruchomiona

Jak to zostało opisane w komentarzu określa (ta zmienna), czy animacja jest aktualnie uruchomiona, czy też nie.

Z początku sama procedura może trochę przestraszyć :)

procedure TMainForm.Go(const PText: TStrings);
var
  PTextLong : Integer; // długość tekstu
  I : Integer;
  LinesCount : Integer; // ilość linii
begin
  while (Running) do  // dopóki zmienna będzie wartości true
  begin
    Application.ProcessMessages;
    if not Running then Break;   // sprawdzaj, czy przypadkiem nic się nie zmieniło?
    for LinesCount := 0 to PText.Count -1 do  // zaczynaj od pierwszej linii
    begin
      if not Running then Break;  // znów sprawdź...
      PTextLong := Length(PText[LinesCount]);  // pobierz długość linii
      Sleep(500); // odczekaj pół sekundy
      Caption := '';  // wymaz Caption
      for I := 0 to PTextLong do   // wykonuj pętle literka po literce
      begin
        Application.ProcessMessages;
        Sleep(100);  // z przerwa 100 milisekund
        if not Running then Break;  // znów sprawdź!
    // wyswietl po kolei wszystkie litery
        Caption := Caption + PText.Strings[LinesCount][i]; 
      end;
    end;
  end;
end;

Jak tak się przyjrzysz to zauważysz, że procedura zawiera trzy pętle zagnieżdżone w sobie. Pierwsza z nich sprawdza, czy zmienna Running ma wartość True - jeżeli tak to animacja jest kontynuowana "w kółko". Często w tej procedurze występuje linia: if not Running then Break;

Sprawdza ten warunek, czy użytkownik nie chce zakończyć działania tej animacji. Jeżeli zmienna ma wartość True - pętla zostaje przerwana. Pierwsza pętla for rozpoczyna działanie na kolejnych liniach tekstu. Kolejna pętla wyświetla na pasku aplikacji kolejne litery. Wcześniej jednak następuje pobranie długości linii ( w znakach ). Później wymazanie tekstu na pasku i same serce animacji:

Caption := Caption + PText.Strings[LinesCount][i]; 

Powoduje to polecenie dodawanie kolejnych liter na pasku. To wszystko. Jeżeli masz jakieś pytania dotyczące tej procedury lub czegoś nie rozumiesz ( czegoś nie wyjaśniłem ) - pisz.

Oto sama procedura wykonywująca powyższą ( Go ):

procedure TMainForm.btnGoClick(Sender: TObject);
var
  sText : TStrings;
begin
  btnStop.Visible := True;  // pokazanie przycisku umozliwiajacego zatrzymanie
  sText := TStringList.Create;
  try
    with sText do
    begin
    { dodanie kolejnych linii, ktore po kolei beda wyswietlane }
      Add('4programmers.net');
      Add('to witryna dla początkujących jak i zaawansowanych programistów.');
      Add('Znajdziesz na niej wiele kodów źródłowych, programów oraz artykułów.');
      Add('Nie wspominam tutaj o kursach, których jest ponad 30...');
      Add('Zajrzyj: WWW.4PROGRAMMERS.NET');
    end;
    Go(sText); // wywolaj procedure z parametrem
  finally
    sText.Free;
  end;
end;

Cóż można tu powiedzieć. Na początek stworzenie odpowiedniej zmiennej i dodanie do niej kolejnych linii tekstu. Na końcu wywołanie procedury. Możesz poniżej ściągnąć kod źródłowy programu, prezentującego tą procedurę.

6 komentarzy

Działa bez zarzutu.
Wystarczy skopiować, zmienić nazwę formatki, dodać przycisk o nazwie BtnGo, kliknąć na BtnGo i usunąć zdublowane zdarzenie.
Pozostałe czynności - według wyżej podanego opisu.
Od wpisu alfyczarnej upłynęło sporo czasu, mam nadzieję, że przez ten długi czas czegoś się nauczył, ale nie programowania, bo ta dziedzina jest zastrzeżona dla ludzi o określonym poziomie inteligencji.

Ty frajerze jebany ty się ucz łatwiejszych rzeczy ty skórwysynu zajebany bo ten program który podałeś nie działa.
Objaśnij najpierw jak tą procedurę uzyskać bo żeby procedury pojawiły się w sekcji private to trzeba kliknąć jakiś komponent i dlaczego w sekcji uses dajesz zmienne var ty skurwysynu głupi.
Odpisz na email [email protected]

Boże, amatorzy, uczcie się najpierw łatwiejszych rzeczy, jak nie rozumiesz dlaczego Ci wyskakują takie błędy i co one oznaczają, to musiałeś skopiować i wkleić kod, a to nie na tym ma polegać. Na przykładzie zobacz jak to ktoś napisał i zrób samemu. Jak będziesz bezmyślnie kopiował to niczego się nie nauczysz...

dopisz sekcje uses e... u ;) i po wpisuje co ma tam byc

Ziziok, a może chodziło o tego vara już istniejącego co jest kilkanaście linijek niżej??
Nie czytałem całego arta, więc się nie wymądrzam, ale chyba w tym może być błąd...

Wiem, że to JA coś żle robię, więc proszę nie krzyczeć, tylko powoli wytłumaczyć. Jak wklejam w interface var to mi wyskakuje (przy kompilacji) błąd:
[Error] Unit1.pas(8): Declaration expected but 'USES' found
[Error] Unit1.pas(13): Undeclared identifier: 'TForm'
[Error] Unit1.pas(31): ';' expected but '.' found
[Error] Unit1.pas(38): Undeclared identifier: 'Canvas'
[Error] Unit1.pas(42): Undeclared identifier: 'Application'
[Error] Unit1.pas(42): Missing operator or semicolon
[Error] Unit1.pas(43): Undeclared identifier: 'Sleep'
[Error] Unit1.pas(44): Undeclared identifier: 'Brush' itp. itd.
Później, gdzie wkleić :
procedure DrawAnim(X, Y : Integer);
procedure SteepBySteep(X, Y : Integer);
Gdy wpisuję Priate {private declarations} wyskakuje kolejny błąd... :(