JavaFX 过渡动画等待
JavaFX Transition animation waiting
这么快,我正在做一个演示计算机绘图方法的程序。我需要创建时间轴或动作历史,例如(placeVertex(x,y)、moveVertex(newX,newY) 等)并迭代(向前和向后,自动或手动)
我已经通过使用命令设计模式实现了这一点,但这些命令中很少有使用转换的。第一个想法是在每个命令之间 setOnFinished
中使用 Condition 接口的锁定、等待和信号,但它导致 gui 冻结。
我尝试了 SequentialTransition,但它对我的问题没有用 - 无法在转换之间动态更改属性。
是否有可能以某种方式通知一代人一个过渡结束,下一个可以 运行 而无需 GUI 冻结和绘制?
谢谢!
编辑:我会尽量简化这一切
这是我的命令界面和这些命令之一:
public interface Command {
public void execute();
}
public class MoveVertex implements Command {
public MoveVertex(Data d, Vertex v, double changedX, double changedY){..};
@Override
public void execute() {
Path path = new Path();
path.getElements().add(new MoveTo(v.getCenterX(), v.getCenterY()));
path.getElements().add(new LineTo(changedX, changedY));
PathTransition pathTransition = new PathTransition();
pathTransition.setDuration(Duration.millis(velocity));
pathTransition.setPath(path);
pathTransition.setNode(v.getVertex());
pathTransition.play(); }
}
这些命令存储在我的历史记录中 class 基本上是
private List<Command> history;
我确实会浏览列表并执行命令
public boolean executeNext() {
if (history.size() != position) {
history.get(position).execute();
position++;
return true;
}
return false;
}
并且我试图在下一个命令开始时达到状态,只有在上一个命令完成的情况下。试图将 await/signal 置于两者之间但没有成功。
下面的解决方案使用了 Itachi 的建议,即提供一个 onFinished 处理程序,以便在我们到达下一个位置后将节点移动到新的(随机)位置。
通过重新使用单个 Transition 而不是在事件处理程序中使用递归,它可能会变得更高效(也更容易理解)。可能没有必要为每个动作创建一个新的 Transition - 但是,只要没有数十万次动作迭代,它应该是可以接受的。
import javafx.animation.*;
import javafx.application.Application;
import javafx.event.*;
import javafx.geometry.Point2D;
import javafx.scene.*;
import javafx.scene.layout.Pane;
import javafx.scene.paint.Color;
import javafx.scene.shape.*;
import javafx.stage.Stage;
import javafx.util.Duration;
import java.util.Random;
// animates moving a node forever in a random walk pattern.
public class RandomWalk extends Application {
private static final Random random = new Random(42);
private static final double W = 200;
private static final double H = 200;
private static final double R = 10;
private static final Node node = new Circle(
R, Color.FORESTGREEN
);
@Override
public void start(Stage stage) {
// start from the center of the screen.
node.relocate(W / 2 - R, H / 2 - R);
stage.setScene(new Scene(new Pane(node), W, H));
stage.show();
walk();
}
// start walking from the current position to random points in sequence.
private void walk() {
final Point2D to = getRandomPoint();
final Transition transition = createMovementTransition(
node,
to
);
transition.setOnFinished(
walkFrom(to)
);
transition.play();
}
private EventHandler<ActionEvent> walkFrom(final Point2D from) {
return event -> {
// Finished handler might be called a frame before transition complete,
// leading to glitches if we relocate in the handler.
// The transition works by manipulating translation values,
// so zero the translation out before relocating the node.
node.setTranslateX(0);
node.setTranslateY(0);
// After the transition is complete, move the node to the new location.
// Relocation co-ordinates are adjusted by the circle's radius.
// For a standard node, the R adjustment would be unnecessary
// as most nodes are located at the top left corner of the node
// rather than at the center like a circle is.
node.relocate(
from.getX() - R,
from.getY() - R
);
// Generate the next random point and play a transition to walk to it.
// I'd rather not use recursion here as if you recurse long enough,
// then you will end up with a stack overflow, but I'm not quite sure
// how to do this without recursion.
final Point2D next = getRandomPoint();
final Transition transition = createMovementTransition(node, next);
transition.setOnFinished(walkFrom(next));
transition.play();
};
}
// We use a PathTransition to move from the current position to the next.
// For the simple straight-line movement we are doing,
// a straight TranslateTransition would have been fine.
// A PathTransition is just used to demonstrate that this
// can work for the generic path case, not just straight line movement.
private Transition createMovementTransition(Node node, Point2D to) {
Path path = new Path(
new MoveTo(
0,
0
),
new LineTo(
to.getX() - node.getLayoutX(),
to.getY() - node.getLayoutY()
)
);
return new PathTransition(
Duration.seconds(2),
path,
node
);
}
// @return a random location within a bounding rectangle (0, 0, W, H)
// with a margin of R kept between the point and the bounding rectangle edge.
private Point2D getRandomPoint() {
return new Point2D(
random.nextInt((int) (W - 2*R)) + R,
random.nextInt((int) (H - 2*R)) + R
);
}
public static void main(String[] args) {
launch(args);
}
}
这么快,我正在做一个演示计算机绘图方法的程序。我需要创建时间轴或动作历史,例如(placeVertex(x,y)、moveVertex(newX,newY) 等)并迭代(向前和向后,自动或手动)
我已经通过使用命令设计模式实现了这一点,但这些命令中很少有使用转换的。第一个想法是在每个命令之间 setOnFinished
中使用 Condition 接口的锁定、等待和信号,但它导致 gui 冻结。
我尝试了 SequentialTransition,但它对我的问题没有用 - 无法在转换之间动态更改属性。
是否有可能以某种方式通知一代人一个过渡结束,下一个可以 运行 而无需 GUI 冻结和绘制?
谢谢!
编辑:我会尽量简化这一切 这是我的命令界面和这些命令之一:
public interface Command {
public void execute();
}
public class MoveVertex implements Command {
public MoveVertex(Data d, Vertex v, double changedX, double changedY){..};
@Override
public void execute() {
Path path = new Path();
path.getElements().add(new MoveTo(v.getCenterX(), v.getCenterY()));
path.getElements().add(new LineTo(changedX, changedY));
PathTransition pathTransition = new PathTransition();
pathTransition.setDuration(Duration.millis(velocity));
pathTransition.setPath(path);
pathTransition.setNode(v.getVertex());
pathTransition.play(); }
}
这些命令存储在我的历史记录中 class 基本上是
private List<Command> history;
我确实会浏览列表并执行命令
public boolean executeNext() {
if (history.size() != position) {
history.get(position).execute();
position++;
return true;
}
return false;
}
并且我试图在下一个命令开始时达到状态,只有在上一个命令完成的情况下。试图将 await/signal 置于两者之间但没有成功。
下面的解决方案使用了 Itachi 的建议,即提供一个 onFinished 处理程序,以便在我们到达下一个位置后将节点移动到新的(随机)位置。
通过重新使用单个 Transition 而不是在事件处理程序中使用递归,它可能会变得更高效(也更容易理解)。可能没有必要为每个动作创建一个新的 Transition - 但是,只要没有数十万次动作迭代,它应该是可以接受的。
import javafx.animation.*;
import javafx.application.Application;
import javafx.event.*;
import javafx.geometry.Point2D;
import javafx.scene.*;
import javafx.scene.layout.Pane;
import javafx.scene.paint.Color;
import javafx.scene.shape.*;
import javafx.stage.Stage;
import javafx.util.Duration;
import java.util.Random;
// animates moving a node forever in a random walk pattern.
public class RandomWalk extends Application {
private static final Random random = new Random(42);
private static final double W = 200;
private static final double H = 200;
private static final double R = 10;
private static final Node node = new Circle(
R, Color.FORESTGREEN
);
@Override
public void start(Stage stage) {
// start from the center of the screen.
node.relocate(W / 2 - R, H / 2 - R);
stage.setScene(new Scene(new Pane(node), W, H));
stage.show();
walk();
}
// start walking from the current position to random points in sequence.
private void walk() {
final Point2D to = getRandomPoint();
final Transition transition = createMovementTransition(
node,
to
);
transition.setOnFinished(
walkFrom(to)
);
transition.play();
}
private EventHandler<ActionEvent> walkFrom(final Point2D from) {
return event -> {
// Finished handler might be called a frame before transition complete,
// leading to glitches if we relocate in the handler.
// The transition works by manipulating translation values,
// so zero the translation out before relocating the node.
node.setTranslateX(0);
node.setTranslateY(0);
// After the transition is complete, move the node to the new location.
// Relocation co-ordinates are adjusted by the circle's radius.
// For a standard node, the R adjustment would be unnecessary
// as most nodes are located at the top left corner of the node
// rather than at the center like a circle is.
node.relocate(
from.getX() - R,
from.getY() - R
);
// Generate the next random point and play a transition to walk to it.
// I'd rather not use recursion here as if you recurse long enough,
// then you will end up with a stack overflow, but I'm not quite sure
// how to do this without recursion.
final Point2D next = getRandomPoint();
final Transition transition = createMovementTransition(node, next);
transition.setOnFinished(walkFrom(next));
transition.play();
};
}
// We use a PathTransition to move from the current position to the next.
// For the simple straight-line movement we are doing,
// a straight TranslateTransition would have been fine.
// A PathTransition is just used to demonstrate that this
// can work for the generic path case, not just straight line movement.
private Transition createMovementTransition(Node node, Point2D to) {
Path path = new Path(
new MoveTo(
0,
0
),
new LineTo(
to.getX() - node.getLayoutX(),
to.getY() - node.getLayoutY()
)
);
return new PathTransition(
Duration.seconds(2),
path,
node
);
}
// @return a random location within a bounding rectangle (0, 0, W, H)
// with a margin of R kept between the point and the bounding rectangle edge.
private Point2D getRandomPoint() {
return new Point2D(
random.nextInt((int) (W - 2*R)) + R,
random.nextInt((int) (H - 2*R)) + R
);
}
public static void main(String[] args) {
launch(args);
}
}