Jak zrobić animację rysowania linii?

0

Witam!!!

Mój kod programu częściowy:

import javafx.application.Application;
import javafx.beans.value.ChangeListener;
import javafx.beans.value.ObservableValue;
import javafx.scene.Group;
import javafx.scene.Scene;
import javafx.scene.canvas.Canvas;
import javafx.scene.canvas.GraphicsContext;
import javafx.scene.control.ComboBox;
import javafx.scene.control.Label;
import javafx.scene.control.TextField;
import javafx.scene.paint.Color;
import javafx.stage.Stage;

/**
 *
 * @author Garm
 */



public class BasicOpsTest extends Application {
    
    
    public static void main(String[] args) {
        launch(args);
    }
 
    @Override
    public void start(Stage primaryStage) {         
        primaryStage.setTitle("Bryły Geometryczne");    
        Group root = new Group();   /
        int sizex =500;             
        int sizey =800;                                    
      
        Canvas canvas = new Canvas(sizex, sizey);  
        drawShapes(gc); 
        root.getChildren().add(canvas);
        primaryStage.setScene(new Scene(root));
        primaryStage.setHeight(900);
        primaryStage.setWidth(1100);
        
        primaryStage.show();
        }
        private void drawShapes(GraphicsContext gc) {
        
        gc.setFill(Color.GREEN);
        gc.setStroke(Color.BLUE);
        gc.setLineWidth(3);
        gc.strokeLine(100,720,440,620);     
        }
 }     
   Chciałbym , aby ktoś zrobił w tym programie animację rysowania linii strokeLine (zastanawiam się jak użyć timer i action listenera i nie mam bladego pojęcia ), może ktoś ma pomysł?

Chodzi mi o to żebym widział jak rysuje program tę linię a nie , że pojawia się od razu.

0

Określ swoją szybkość rysowania (w pixelach), co określony interwał zwiększaj swój "przebyły" punkt.
Ot, cała magia.

Przykłady:
http://stackoverflow.com/questions/10126590/animated-lines-in-java-swing (brzydki jakiś, mniejsza)
http://stackoverflow.com/questions/5010097/animating-a-line-between-two-point-objects

0

Czy mógłbyś ktoś napisać kod do tego przykładu? Nie mam zielonego pojęcia, jak to zrobić dla kontrolki canvas, gdzie mam być ten Timer i ActionListener, gdzie włożyć kod obliczający kolejne punkty dla linii, a gdzie ma się wywołać odświeżanie przerysowania komponentu. Animacja ma pokazywać jakbyśmy rysowali linię na ekranie w danej chwili. Byłbym wdzięczny jakby ktoś uzupełnił ten kod powyżej.

0

Sposób działania animacji jest dość prosty co rysujesz, za chwilę wymazujesz i rysujesz ponownie coś nowego, i ponownie wymazujesz itd. Chodzi o to żeby czas kiedy coś jest narysowane był jak najdłuższy, a okres kiedy jest wymazane i jeszcze nie narysowane był jak najkrótszy bo dzięki temu ludzkie oko nie zarejestruje momentu kiedy nie będzie nic narysowane, czyli nie zobaczy migotania.
W Twoim przypadku cel jest taki, żeby narysować linię o współrzędnych x1, y1 -> x1, y1, co daje pojedynczy punkt, wymazać ją, a następnie ponownie rysować linię o współrzędnych x1, y1 -> x1', y1', gdzie oba te punkty są położone na prostej łączącej x1, y1 i x2, y2 - czyli w kierunku miejsca docelowego. Aż x1', y1' staną się równe x2 i y2. Pierwszą rzeczą, którą musisz sobie określić jest na ile klatek animacji chcesz podzielić ten okres między narysowaniem punktu, a linii w wersji docelowej, czyli tej która osiągnie x2, y2. Czy czas animacji ma być stały czy zależny od docelowej długości linii?
Jeżeli stały, np. 100 klatek, to musisz wyliczyć sto par punktów x1', y1' na prostej, które będą dążyć do docelowej ostatniej pary (x1', y1') = (x2, y2). Ten zestaw punktów najlepiej wyliczyć sobie wcześniej, chociaż można też wyliczać na bieżąco z każdą klatką animacji. Jeżeli czas animacji ma być zmienny i zależny od docelowej długości linii, to musisz sobie określić krok z jakim będziesz wyszukiwał, żeby następnie określić ilość punktów, a tym samym klatek animacji. Możesz jako krok określić ilość pikseli na dłuższej składowej x lub y, albo połowę tej liczby ponieważ w przeciwnym wypadku co druga klatka może wskazywać na identyczne wartości. Na przykład jeżeli |x2 - x1|==135, a |y2 - y1|==43, to możesz sobie ustalić 135/2 = 67 + 1 = 68 klatek. Dzięki temu różnice końcówek animowanej linii będą się zmieniać o nieco więcej niż piksel w pionie lub poziomie, ale nie więcej niż 2 piksele. Ponieważ piksele, to tablica punktów, więc punkt końcowy i sama linia jest rysowana jako kolory docelowe (np. czarne) bliżej idealnego środka linii i jako kolory pośrednie między docelowymi, a podłożem tła (np. białym). Dzięki temu mimo iż linia to schodki, to nam wydaje się jakby linia narysowana pod kątem była idealnie równa (czasem widoczna jako nieco grubsza).

Jeżeli masz już ustaloną liczbę klatek i sposób na wyliczenie pośrednich końcówek (lub są już wyliczone i czekają sobie w jakiejś tablicy/liście, to możesz zaimplementować samą animację.
Początkiem animacji jest oczywiście jakaś akcja - na przykład kliknięcie myszą w punkt docelowy lub cokolwiek innego.
Animacja jako taka jest synchroniczna - czyli my wymuszamy kolejne kroki aż się nie skończą, ale GUI i jego system zdarzeń są asynchroniczne - czyli pojawiają się kiedy coś nastąpi. Jeżeli zlecamy przerysowanie czegoś, to tylko to zlecamy i sterowanie od razu wraca do zleceniodawcy. Dlatego metoda paint jest wywoływana tylko w reakcji na konieczność przerysowania kawałka elementu, którym się zajmuje - na przykład po odkryciu uprzednio zasłoniętego kawałka obrazu. Do animacji to się kompletnie nie nadaje.
Metoda paint też nie powinna niczego liczyć. Powinny być w niej wyłącznie polecenia rysujące - najlepiej z wyliczonych już współrzędnych, które trzeba tylko pobrać. Na przykład gdy w paint jedynym poleceniem jest narysowanie linii (x1, y1), (x1', y1'), a x1, y1, x1', y1' są zmiennymi dostępnymi z metody paint, to każde wywołanie paint narysuje tę linię.
Musimy więc zmusić gui do takiego działania, że najpierw zmieniamy współrzędne x1' i y1', a następnie zmuszamy coś do ponownego wywołania paint., następnie odczekujemy czas ustalony dla jednej klatki i powtarzamy to jeszcze raz.
Zmianę zawartości zmiennych końcówek trzeba z każdą klatką podmieniać, a następnie wymuszać przemalowanie. Czyli jakiś kod, który to będzie robić będzie musiał być wykonywany cyklicznie co jakiś czas. I do tego właśnie przydaje się timer lub jaki scheduler. Powtarza on co jakiś czas wykonanie jakiegoś kodu. W przypadku timera będzie to kod metody run klasy dziedziczącej po TimerTask, a w przypadku schedulera kod tej samej metody, ale klasy implementującej Runnable.
Kod takiej metody musi być bardzo prosty - zmiana wartości dwóch zmiennych x1' i y1' oraz wywołanie metody repaint dla obiektu, który chcemy przemalować. Metoda repaint wymusza na gui wywołanie metody paint tak szybko jak będzie to dla systemu gui możliwe. I dlatego metoda paint powinna być jak najkrótsza i jak najszybsza. Bo jeżeli taka nie będzie, to zanim się skończy jej wykonywanie timer już wywoła kolejne wywołanie zmiany współrzędnych oraz repaint. Krótko mówiąc jeżeli rysowanie nie zakończy się przed początkiem następnej klatki, to rysowanie będzie albo opóźnione, albo pominięte. Oba przypadki są do d**y. :)

Można też oczywiście próbować wywołać w pętli metodę paintImmediately, następnie polecenie odczekania jakiegoś odcinka czasu i kolejnej iteracji, ale jest to o tyle do kitu, że w tym czasie system gui jest kompletnie nie responsywny - nie robi swoich zadań, a tym samym nie odpowiada na żadne zmiany stanu takie jak akcje myszki lub klawiatury. Tego typu animacje robią osoby początkujące i nie rozumiejące asynchronicznego sposob działania gui.

Co powinno się stać jeżeli punkt docelowy zmieni się zanim animacja zostanie w całości ukończona? Tak dzieje się jeżeli myszką klikniemy w nowy punkt. Wtedy animacja powinna być przerwana, przeliczone nowe punkty dla kolejnego zestawu klatek oraz ponownie rozpoczęta od punktu początkowego. To powinna być zawartość metody listenera nasłuchującego zdarzenia np. kliknięcia myszką (mouse pressed) czy np. buttona (action event) gdy wprowadziliśmy w jakiejś kontrolce nowe współrzędne.

Jest jeszcze kwestia rysowania i wymazywania.
Są dwie techniki pierwsza i wolniejsza polega na tym aby przed narysowaniem czegokolwiek co ma być wymazane zachować pierwotną postać mapy bitowej w tym miejscu, czyli w przypadku rysowania linii należy zapisać prostokąt kontrolki/ekranu o współrzędnych x1, y1, x2, y2 (to nie ważne, że początkowe klatki animacji nie potrzebują tak dużego obszaru), a następnie jako operację wymazywania przywrócić tę zawartość w oryginalne miejsce. Rysowanie, to oczywiście zwykłe polecenie rysowania dostępne w systemie graficznym. To jest preferowane podejście gdy animowany element jest bardzo skomplikowany lub animacja obejmuje zmienność kolorów lub sprite'y. Jeżeli elementami animowanymi są rzeczy, które da się narysować z systemu graficznego, a kwestia kolorów nie jest kluczowa, to można do wymazywanie użyć tej samej sekwencji, która animację narysowała, ale uprzednio przed rysowaniem i wymazywaniem ustawiając flagę pracy w trybie XOR. Jest to tryb korzystający w operacji xor, która powoduje, że dwukrotne narysowanie tego samego elementu powoduje narysowanie i wymazanie go - przywrócenie poprzedniej zawartości pikseli w miejscu rysowania. Jest to opcja najszybsza i najbardziej polecana gdy elementami animowanymi są proste figury geometryczne lub ich złożenie, a obraz jest głównie monochromatyczny (np. biały i czarny). W przypadku kolorów ich określenie w miejscach przecięć jest trudne ponieważ kolor będzie wartością wynikającą z podmiany bitów operacją xor w składowych wartości koloru piksela.

Mając te informacje powinieneś już umieć samodzielnie napisać taki kod.

1 użytkowników online, w tym zalogowanych: 0, gości: 1