我正在做一个项目,该项目具有用直线为路径设置动画的功能。因此,当我按下按钮时,一条线穿过画布中的所有圆圈。但我发现我的路线和笔触线略有不同。它不会通过圈子。请恳请指教,谢谢。 这是我的代码:
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);
}
}
这是问题。在某些拐角处,描边线不会穿过圆弧,并且会进行怪异的切割。
答案 0 :(得分:0)
我最初无法复制它,但是经过更多尝试后,我设法复制了它。看来这是由于延迟造成的。
据我所知(我不是动画专家),动画(包括PathTransition
)在内部使用计时器,每个刻度通常等效于JavaFX场景图脉冲持续时间。这意味着动画每秒最多更新60次。当发生脉冲时,PathTransition
根据计时器上经过的时间来计算新的转换值。因此,这里发生的是,计算出的转换值在两个特定脉冲之间相距很远。通常,您尝试在JavaFX Application Thread上执行的操作越多,发生这种情况的可能性就越大。
如果您尝试根据动画绘制描线,则无法避免这种延迟。但是,通过将其分解为多个动画,可以避免绘制的路径出现拐角的问题。每个动画都将沿一条直线移动,并且保证您的每条描边线都从起点开始,到终点为止。
我试过了,它有效:
// 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;
}
唯一改变的是速度在完整路径的每个段上都改变了。如果要保持该常数不变,则必须应用毕达哥拉斯定理来找出距离,并相应地调整持续时间。