Язык программирования:
Java
Среда программирования:
IntelliJ IDEA 2016
package me.grafika.ivleva; import javafx.animation.*; import javafx.application.Application; import javafx.scene.Group; import javafx.scene.Scene; import javafx.scene.paint.Color; import javafx.scene.shape.Circle; import javafx.scene.shape.LineTo; import javafx.scene.shape.MoveTo; import javafx.scene.shape.Path; import javafx.stage.Stage; import javafx.util.Duration; import java.util.ArrayList; import static java.lang.Math.*; public class BreathingCircles extends Application { private static final double RADIUS = 46; // радиус одного круга private static final double MOVE_DURATION_MS = 1000; // время выполнения одного перемещения private static final double PAUSE_DURATION_MS = 300; // пауза между перемещениями private static final double TOTAL_DURATION = MOVE_DURATION_MS + PAUSE_DURATION_MS; // общее время выполнения @Override public void start(Stage primaryStage) throws Exception { Group root = new Group(); // корневой контейнер primaryStage.setTitle("Breathing Circles"); // задаем заголовок окна primaryStage.setScene(new Scene(root, 500, 500, Color.BLACK)); primaryStage.setResizable(false); // запрещаем изменять размер окна Group circles = new Group(); // сюда сохраним круги ArrayList<SequentialTransition> transitions = new ArrayList<>(); // а сюда - всю анимацию // считаем, сколько кругов по вертикали и горизонтали нам хватит int circles_in_raw = (int) ceil(500 / RADIUS) + 2; int circles_in_column = (int) ceil(500 / sqrt(3) / RADIUS) + 2; // создаем в два раза больше кругов (из-за движения) for (int x = -circles_in_raw; x <= circles_in_raw; x++) { for (int y = -circles_in_column; y <= circles_in_column; y++) { Circle circle = new Circle(getX(x, y), getY(x, y), RADIUS, Color.TRANSPARENT); // создаем прозрачный круг circle.setStroke(Color.WHITE); // устанавливаем белую обводку circle.setStrokeWidth(2); // шириной в 2 пикселя circles.getChildren().add(circle); // сохраняем if (x == 0 && y == 0) { // если это центральный круг - пропускаем, иначе создаем анимацию continue; } SequentialTransition transition = new SequentialTransition( // пауза new PauseTransition(Duration.millis(PAUSE_DURATION_MS / 2)), // перемещение new PathTransition( Duration.millis(MOVE_DURATION_MS), new Path( new MoveTo(getX(x, y), getY(x, y)), // перемещаемся в точку посередине между окружностью и центром сцены new LineTo( (getX(x, y) + getX(0, 0)) / 2, (getY(x, y) + getY(0, 0)) / 2 ) ), circle ), // пауза new PauseTransition(Duration.millis(PAUSE_DURATION_MS / 2)) ); transition.setAutoReverse(true); // запрашиваем обратную анимацию transition.setCycleCount(Animation.INDEFINITE); // выполняется бесконечно transition.setInterpolator(Interpolator.EASE_BOTH); // начать и закончить медленно transitions.add(transition); // сохраняем } } root.getChildren().add(circles); // добавляем круги primaryStage.show(); // запускаем сцену // максимальное расстояние от центра видимого после движения круга до центра сцены // (по отношению к нему перематываем анимацию) double max_distance = sqrt(pow(getX(circles_in_raw, 0) - 250, 2) + pow(getY(circles_in_raw, 0) - 250, 2)); for (SequentialTransition st : transitions) { PathTransition pt = (PathTransition) st.getChildren().get(1); // получаем путь Circle circle = (Circle) pt.getNode(); // получаем круг double cur_distance = sqrt(pow(circle.getCenterX() - 250, 2) + pow(circle.getCenterY() - 250, 2)); double ratio = cur_distance / max_distance; // вычисляем % выполнения анимации st.jumpTo(Duration.millis(PAUSE_DURATION_MS / 2 + MOVE_DURATION_MS * ratio)); // перематываем анимацию } transitions.forEach(SequentialTransition::play); // запускаем анимацию } // Заметим, что можно ввести аффинное преобразование, переводящее декартовую систему координат в точки центров // окружностей, так, чтобы (0,0) переходил в (250,250), (1,1) в (250+sqrt(3)*R,250+3R) и т.д. // Соответсвующее преобразование: // x_center = 250 + x*R + y*2R // y_center = 250 + x*sqrt(3) * R private double getX(double x, double y) { return 250 + x * RADIUS + y * 2 * RADIUS; } private double getY(double x, double y) { return 250 + x * sqrt(3) * RADIUS; } }
Прикрепленный файл | Размер |
---|---|
BreathingCircles.zip | 24.38 кб |