JavaFx анимация пути с помощью формы
Я работаю над проектом с функцией анимации пути с помощью линии. Поэтому, когда я нажимаю кнопку, линия проходит через все круги на холсте. Но я вижу, что есть небольшая разница в моем пути и линии хода. Это не проходит через круги. Пожалуйста, добрый совет, спасибо. Это мой код:
import java.util.ArrayList;
import java.util.Random;
import javafx.animation.Animation;
import javafx.animation.PathTransition;
import javafx.animation.PathTransition.OrientationType;
import javafx.application.Application;
import javafx.beans.value.ChangeListener;
import javafx.beans.value.ObservableValue;
import javafx.scene.Scene;
import javafx.scene.canvas.Canvas;
import javafx.scene.canvas.GraphicsContext;
import javafx.scene.control.Button;
import javafx.scene.control.TextField;
import javafx.scene.layout.BorderPane;
import javafx.scene.layout.Pane;
import javafx.scene.layout.VBox;
import javafx.scene.paint.Color;
import javafx.scene.shape.Circle;
import javafx.scene.shape.CubicCurveTo;
import javafx.scene.shape.LineTo;
import javafx.scene.shape.MoveTo;
import javafx.scene.shape.Path;
import javafx.scene.shape.Rectangle;
import javafx.stage.Stage;
import javafx.util.Duration;
public class PathVisualization extends Application {
private static double SCENE_WIDTH = 1000;
private static double SCENE_HEIGHT = 500;
private Canvas canvas;
private GraphicsContext gc;
private Button clear;
private Animation animation;
private ArrayList<Circle> num;
@Override
public void start(Stage primaryStage) throws Exception {
BorderPane bp = new BorderPane();
Pane root = new Pane();
root.setMaxSize(800,500);
// path = new Path();
canvas = new Canvas(SCENE_WIDTH,SCENE_HEIGHT);
TextField no = new TextField();
Button add = new Button("Add Circle");
Button playAnimation = new Button("Play");
clear = new Button("Clear");
Button pause = new Button("Pause");
VBox control = new VBox();
control.getChildren().add(no);
control.getChildren().add(add);
control.getChildren().add(playAnimation);
control.getChildren().add(clear);
control.getChildren().add(pause);
root.getChildren().add(canvas);
bp.setCenter(root);
bp.setLeft(control);
num = new ArrayList<Circle>();
clear.setOnMouseClicked(event->{
num.clear();
gc.clearRect(0, 0, gc.getCanvas().getWidth(), gc.getCanvas().getHeight());
root.getChildren().clear();
root.getChildren().add(canvas);
});
add.setOnMouseClicked(event->{
int cirNum = Integer.parseInt(no.getText());
Random rand = new Random();
for (int j=0;j<cirNum;j++) {
int x = rand.nextInt((int)root.getMaxWidth()-1);
int y = rand.nextInt((int)root.getMaxHeight()-1);
Circle circle = new Circle(x,y,10);
num.add(circle);
root.getChildren().add(circle);
}
//Heuristic Change
Path path = new Path();
path.setStroke(Color.RED);
path.getElements().add(new MoveTo(num.get(0).getCenterX(),num.get(0).getCenterY()));
path.setStrokeWidth(1);
for (int i=0;i<num.size();i++) {
try {
path.getElements().addAll(new LineTo(num.get(i).getCenterX(),num.get(i).getCenterY()));
}catch(IndexOutOfBoundsException e) {
path.getElements().addAll(new LineTo(num.get(0).getCenterX(),num.get(0).getCenterY()));
}
}
root.getChildren().addAll(path);
//create animation
animation = createPathAnimation(path, Duration.seconds(num.size()/4));
});
playAnimation.setOnMouseClicked(e ->{
animation.play();
});
pause.setOnMouseClicked(e->{
animation.pause();
});
primaryStage.setScene(new Scene(bp, SCENE_WIDTH, SCENE_HEIGHT));
primaryStage.show();
}
private Animation createPathAnimation(Path path, Duration duration) {
gc = canvas.getGraphicsContext2D();
// move a node along a path. we want its position
Circle pen = new Circle(0,0,0);
// create path transition
PathTransition pathTransition = new PathTransition(duration, path, pen);
pathTransition.currentTimeProperty().addListener( new ChangeListener<Duration>() {
Location oldLocation = null;
/**
* Draw a line from the old location to the new location
*/
@Override
public void changed(ObservableValue<? extends Duration> observable, Duration oldValue, Duration newValue) {
// skip starting at 0/0
if( oldValue == Duration.ZERO)
return;
// get current location
double x = pen.getTranslateX();
double y = pen.getTranslateY();
// initialize the location
if( oldLocation == null) {
oldLocation = new Location();
oldLocation.x = x;
oldLocation.y = y;
return;
}
// draw line
gc.setStroke(Color.GREENYELLOW);
gc.setLineWidth(5);
gc.strokeLine(oldLocation.x, oldLocation.y, x, y);
// update old location with current one
oldLocation.x = x;
oldLocation.y = y;
}
});
pathTransition.setOrientation(OrientationType.ORTHOGONAL_TO_TANGENT);
return pathTransition;
}
public static class Location {
double x;
double y;
}
public static void main(String[] args) {
launch(args);
}
}
Это проблема. В определенных углах штриховая линия не проходит через круг и делает странный разрез.
1 ответ
Я не мог воспроизвести это первоначально, но после большего количества попыток мне удалось воспроизвести это. Похоже, это связано с задержкой.
Насколько я знаю (я не эксперт в анимации), анимации (в том числе PathTransition
) использовать таймер внутри себя, и каждый тик обычно эквивалентен длительности импульса графа сцены JavaFX. Это означает, что анимация обновляется максимум 60 раз в секунду. Когда происходит импульс, PathTransition
вычисляет новые значения перевода на основе прошедшего времени на таймере. Итак, что здесь происходит, так это то, что вычисленные значения перевода довольно далеко друг от друга между двумя конкретными импульсами. В общем, чем больше вещей вы пытаетесь сделать в потоке приложений JavaFX, тем больше вероятность того, что это произойдет.
Если вы пытаетесь нарисовать линию трассировки на основе анимации, вы не сможете избежать этой задержки. Тем не менее, вы можете избежать проблемы прорисовки контуров, разбив их на несколько анимаций. Каждая анимация будет двигаться по прямой линии, и каждая из ваших линий трассировки гарантированно будет начинаться с начала и до конца в конечной точке.
Я попробовал это, и это работает:
// Heuristic Change
// Path path = new Path();
// path.setStroke(Color.RED);
// path.getElements().add(new MoveTo(num.get(0).getCenterX(), num.get(0).getCenterY()));
// path.setStrokeWidth(1);
// for (int i = 0; i < num.size(); i++) {
// try {
// path.getElements().addAll(new LineTo(num.get(i).getCenterX(), num.get(i).getCenterY()));
// }
// catch (IndexOutOfBoundsException e) {
// path.getElements().addAll(new LineTo(num.get(0).getCenterX(), num.get(0).getCenterY()));
// }
// }
List<Path> paths = new ArrayList<>();
for (int i = 0; i < num.size() - 1; i++) {
Circle current = num.get(i);
Circle next = num.get(i + 1);
Path path = new Path();
path.setStroke(Color.RED);
path.getElements().addAll(new MoveTo(current.getCenterX(), current.getCenterY()),
new LineTo(next.getCenterX(), next.getCenterY()));
path.setStrokeWidth(1);
paths.add(path);
}
// root.getChildren().addAll(path);
root.getChildren().addAll(paths);
Circle pen = new Circle();
// create animation
// animation = createPathAnimation(path, Duration.seconds(num.size()/4));
animation = createPathAnimation(paths, Duration.millis(200), pen);
А также...
private Animation createPathAnimation(List<Path> paths, Duration duration, Circle pen) {
SequentialTransition seq = new SequentialTransition();
// ...
for (Path path : paths) {
// The same PathTransition stuff you had
seq.getChildren().add(pathTransition);
}
return seq;
}
Единственное, что изменилось, это то, что скорость изменяется на каждом отрезке полного пути. Если вы хотите сохранить эту константу, вы должны применить теорему Пифагора, чтобы найти расстояние, и соответствующим образом скорректировать продолжительность.