JavaFX: seryjne migawki (gif) z węzła (Node
)
W JavaFX 14 migawki możemy tworzyć ze sceny (Scene
), z węzła (Node
) oraz z ekranu (Robot
).
W tym wpisie pokazuję jak utworzyć i zapisać na dysku seryjne migawki z węzła Node
.
Mogą być podstawą do utworzenia animowanego GIF-a, czy innej animacji.
Klasa Listing07c_02
Ściągnij klasę Listing07c_02
package rozdzial07c; import javafx.animation.AnimationTimer; import javafx.application.Application; import javafx.embed.swing.SwingFXUtils; import javafx.geometry.Rectangle2D; import javafx.scene.Scene; import javafx.scene.SnapshotParameters; import javafx.scene.image.WritableImage; import javafx.scene.layout.StackPane; import javafx.scene.paint.Color; import javafx.scene.shape.Circle; import javafx.stage.Stage; import javax.imageio.ImageIO; import java.awt.image.BufferedImage; import java.io.File; import java.io.IOException; import java.time.Instant; public class Listing07c_02 extends Application { private double radius = 30; private int counter; private StackPane root; private Circle circle; private SnapshotParameters params; @Override public void start(Stage stage) { root = new StackPane(); //- counter = 1; params = new SnapshotParameters(); params.setFill(Color.WHITE); params.setViewport(new Rectangle2D(0, 0, 270, 300)); //- circle = new Circle(radius); circle.setFill(Color.DARKCYAN); root.getChildren().add(circle); AnimationTimer timer = new MyTimer(); timer.start(); Scene scene = new Scene(root, 300, 300); stage.setTitle("AnimationTimer"); stage.setScene(scene); stage.show(); } private class MyTimer extends AnimationTimer { @Override public void handle(long now) { WritableImage wi = root.snapshot(params, null); File file = new File( "rozdzial07c/src/rozdzial07c/gifs/rys07_" + leadingZeroes(2, counter) + ".gif"); BufferedImage bi = SwingFXUtils.fromFXImage(wi, null); try { ImageIO.write(bi, "gif", file); counter++; System.out.println(Instant.now()); } catch (IOException e) { e.printStackTrace(); } radius -= 0.5; circle.radiusProperty().set(radius); if (radius <= 0) { radius = 30.0; circle.radiusProperty().set(radius); stop(); System.out.println("Animation stopped"); } } } private static String leadingZeroes(int digits, int number) { return String.format("%0" + String.valueOf(digits) + "d", number); } public static void main(String[] args) { Application.launch(args); } }
Automatyczna numeracja gifów stwarza problem. Chociaż numery są po kolei i obrazki w IDE są listowane w prawidłowej kolejności, to po pobraniu obrazków przez klasy Java w celu wykonania animowanego gifa obrazki nie są już ułożone po kolei, np. po 1 następuje 10, 11, …, 2, 21, 22, itd. Aby rozwiązać ten problem trzeba dodać zera wiodące. Czyni to metoda leadingZeroes
, w której podajemy maksymalna liczbę cyfr w numerze oraz numer do którego należy dodać zera.
private static String leadingZeroes(int digits, int number) { return String.format("%0" + String.valueOf(digits) + "d", number); }
Po uruchomieniu klasy w folderze gifs zobaczymy automatycznie wykonane zrzuty ekranu wraz z prawidłowo ponumerowanymi obrazkami.
Przykładowy wygląd zrzutu z węzła (Rys. 07_03):
Przykładowy gif (rys07_04):
Problemem tej animacji jest oczywiście fakt, że nie kontrolujemy ani liczby ani odstępów czasowych pomiędzy wykonaniem kolejnych migawek. Problem ten rozwiążemy w innym przykładzie.
Na konsoli widzimy czasy pobrania kolejnych migawek.
... 2021-01-08T09:36:37.487456700Z 2021-01-08T09:36:37.510458100Z 2021-01-08T09:36:37.537459600Z 2021-01-08T09:36:37.559460900Z 2021-01-08T09:36:37.588462500Z 2021-01-08T09:36:37.610463800Z 2021-01-08T09:36:37.638465400Z Animation stopped