[英]JavaFX animation not moving the Node
I am currently developing a small JavaFX library to show svg content.我目前正在开发一个小型 JavaFX 库来显示 svg 内容。 I am in the process of implementing animations.
我正在实现动画。 They work correctly, except for
animate
or animateTransform
for x and y positions, and I don't understand why.它们工作正常,除了 x 和 y 位置的
animate
或animateTransform
,我不明白为什么。
When I am doing this, it works flawlessly:当我这样做时,它完美无缺:
public class TimelineTest extends Application {
public static void main(String[] args) {
launch(args);
}
public void start(Stage primaryStage) {
Group root = new Group();
Scene scene = new Scene(root, 800, 600);
primaryStage.setScene(scene);
Rectangle rect = new Rectangle(100, 100, 50, 50);
root.getChildren().add(rect);
rect.setFill(Color.BLUE);
Timeline timeline = new Timeline();
KeyFrame kf0 = new KeyFrame(Duration.ZERO, new KeyValue(rect.xProperty(), 0));
KeyFrame kf1 = new KeyFrame(Duration.seconds(10), new KeyValue(rect.xProperty(), 100));
timeline.getKeyFrames().addAll(kf0, kf1);
timeline.play();
primaryStage.show();
}
} }
But when I am doing what I think is exactly the same thing in my library, the rectangle seems to twitch a little but not really move.但是当我在我的库中做我认为完全一样的事情时,矩形似乎有点抽搐但并没有真正移动。
My code is:我的代码是:
WritableValue value = null;
switch (nodeName) {
case "rect":
Rectangle rect = (Rectangle) node;
switch (attrName) {
case X:
value = rect.xProperty();
break;
case Y:
value = rect.yProperty();
break;
case WIDTH:
value = rect.widthProperty();
break;
case HEIGHT:
value = rect.heightProperty();
break;
}
break;
}
Timeline timeline = new Timeline();
KeyValue fromValue = new KeyValue(value, 10);
KeyValue toValue = new KeyValue(value, 100);
KeyFrame fromFrame = new KeyFrame(Duration.ZERO, fromValue);
KeyFrame toFrame = new KeyFrame(Duration.seconds(10), toValue);
timeline.getKeyFrames().addAll(fromFrame, toFrame);
Strangely when I'm doing the same thing with the width or height property, it works without any problem.奇怪的是,当我对 width 或 height 属性做同样的事情时,它没有任何问题。
I suspect that my scene is not created correctly (I checked that I am doing all of this in the platform Thread), but everything else is working without any problem.我怀疑我的场景没有正确创建(我检查了我是否在平台线程中执行所有这些操作),但其他一切都正常工作。
If I am trying to use a TranslateTransition
instead, I have exactly the same problem.如果我尝试改用
TranslateTransition
,我会遇到完全相同的问题。
After some comments on this question here, I now undestand why I have this problem (but not how to fix it, at least for now).在这里对这个问题发表了一些评论后,我现在不明白为什么我会遇到这个问题(但不知道如何解决它,至少现在是这样)。 I put the JavaFX content in a ScrollPane.
我将 JavaFX 内容放在 ScrollPane 中。 I did not think that it would be relevant in this case, but it is.
我不认为它在这种情况下是相关的,但它是相关的。 The code is:
代码是:
VBox vBox = new VBox(node);
vBox.setAlignment(Pos.CENTER);
Group group = new Group(vBox );
StackPane content = new StackPane(group);
group.layoutBoundsProperty().addListener((observable, oldBounds, newBounds) -> {
content.setMinWidth(newBounds.getWidth());
content.setMinHeight(newBounds.getHeight());
});
ScrollPane scrollPane = new ScrollPane(content);
scrollPane.setPannable(true);
scrollPane.setFitToHeight(true);
scrollPane.setFitToWidth(true);
scrollPane.setPrefSize(500, 500);
I used the following StackOverflow answer for the ScrollPane: JAVAFX zoom, scroll in ScrollPane我对 ScrollPane 使用了以下 StackOverflow 答案: JAVAFX zoom, scroll in ScrollPane
Here is a reproductible example showing everything:这是一个显示所有内容的可复制示例:
public class TestAnimationInScroll extends Application {
public static void main(String[] args) {
launch(args);
}
public void start(Stage primaryStage) {
Rectangle rect = new Rectangle(100, 100, 50, 50);
rect.setFill(Color.BLUE);
Group root = new Group();
root.getChildren().add(rect);
VBox vBox = new VBox(root);
vBox.setAlignment(Pos.CENTER);
Group group = new Group(root);
StackPane content = new StackPane(group);
group.layoutBoundsProperty().addListener((observable, oldBounds, newBounds) -> {
content.setMinWidth(newBounds.getWidth());
content.setMinHeight(newBounds.getHeight());
});
ScrollPane scrollPane = new ScrollPane(content);
scrollPane.setPannable(true);
scrollPane.setFitToHeight(true);
scrollPane.setFitToWidth(true);
scrollPane.setPrefSize(500, 500);
Timeline timeline = new Timeline();
KeyFrame kf0 = new KeyFrame(Duration.ZERO, new KeyValue(rect.xProperty(), 0));
KeyFrame kf1 = new KeyFrame(Duration.seconds(10), new KeyValue(rect.xProperty(), 100));
timeline.getKeyFrames().addAll(kf0, kf1);
timeline.play();
content.setOnScroll(new EventHandler<ScrollEvent>() {
public void handle(ScrollEvent event) {
double zoomFactor = 1.05;
double deltaY = event.getDeltaY();
if (deltaY < 0) {
zoomFactor = 1 / zoomFactor;
}
Bounds groupBounds = group.getBoundsInLocal();
final Bounds viewportBounds = scrollPane.getViewportBounds();
double valX = scrollPane.getHvalue() * (groupBounds.getWidth() - viewportBounds.getWidth());
double valY = scrollPane.getVvalue() * (groupBounds.getHeight() - viewportBounds.getHeight());
group.setScaleX(group.getScaleX() * zoomFactor);
group.setScaleY(group.getScaleY() * zoomFactor);
Point2D posInZoomTarget = group.parentToLocal(new Point2D(event.getX(), event.getY()));
Point2D adjustment = group.getLocalToParentTransform().deltaTransform(posInZoomTarget.multiply(zoomFactor - 1));
scrollPane.layout();
scrollPane.setViewportBounds(groupBounds);
groupBounds = group.getBoundsInLocal();
scrollPane.setHvalue((valX + adjustment.getX()) / (groupBounds.getWidth() - viewportBounds.getWidth()));
scrollPane.setVvalue((valY + adjustment.getY()) / (groupBounds.getHeight() - viewportBounds.getHeight()));
}
});
Scene scene = new Scene(scrollPane, 800, 600);
primaryStage.setScene(scene);
primaryStage.show();
}
}
As you can see, it is possible to zoom in the ScrollPane, but the effect of the animation is not visible.可以看到,ScrollPane可以放大,但是animation的效果是看不到的。
If I use a ScaleTransition in the same context, it works, such as:如果我在相同的上下文中使用 ScaleTransition ,它可以工作,例如:
public class TestAnimationInScroll extends Application {
public static void main(String[] args) {
launch(args);
}
public void start(Stage primaryStage) {
Rectangle rect = new Rectangle(100, 100, 50, 50);
rect.setFill(Color.BLUE);
Group root = new Group();
root.getChildren().add(rect);
VBox vBox = new VBox(root);
vBox.setAlignment(Pos.CENTER);
Group group = new Group(root);
StackPane content = new StackPane(group);
group.layoutBoundsProperty().addListener((observable, oldBounds, newBounds) -> {
content.setMinWidth(newBounds.getWidth());
content.setMinHeight(newBounds.getHeight());
});
ScrollPane scrollPane = new ScrollPane(content);
scrollPane.setPannable(true);
scrollPane.setFitToHeight(true);
scrollPane.setFitToWidth(true);
scrollPane.setPrefSize(500, 500);
ScaleTransition transition = new ScaleTransition(Duration.seconds(5), rect);
transition.setFromX(1d);
transition.setFromY(1d);
transition.setToX(3d);
transition.setToY(3d);
transition.play();
content.setOnScroll(new EventHandler<ScrollEvent>() {
public void handle(ScrollEvent event) {
double zoomFactor = 1.05;
double deltaY = event.getDeltaY();
if (deltaY < 0) {
zoomFactor = 1 / zoomFactor;
}
Bounds groupBounds = group.getBoundsInLocal();
final Bounds viewportBounds = scrollPane.getViewportBounds();
double valX = scrollPane.getHvalue() * (groupBounds.getWidth() - viewportBounds.getWidth());
double valY = scrollPane.getVvalue() * (groupBounds.getHeight() - viewportBounds.getHeight());
group.setScaleX(group.getScaleX() * zoomFactor);
group.setScaleY(group.getScaleY() * zoomFactor);
Point2D posInZoomTarget = group.parentToLocal(new Point2D(event.getX(), event.getY()));
Point2D adjustment = group.getLocalToParentTransform().deltaTransform(posInZoomTarget.multiply(zoomFactor - 1));
scrollPane.layout();
scrollPane.setViewportBounds(groupBounds);
groupBounds = group.getBoundsInLocal();
scrollPane.setHvalue((valX + adjustment.getX()) / (groupBounds.getWidth() - viewportBounds.getWidth()));
scrollPane.setVvalue((valY + adjustment.getY()) / (groupBounds.getHeight() - viewportBounds.getHeight()));
}
});
Scene scene = new Scene(scrollPane, 800, 600);
primaryStage.setScene(scene);
primaryStage.show();
}
}
Do somebody have a clue of where is my mistake?有人知道我的错误在哪里吗? Perhaps there is a way to still be able to pan and zoom in the scrollPane but still seeing correctly the animations?
也许有一种方法可以在 scrollPane 中平移和缩放,但仍然可以正确看到动画?
I understand why I have this problem.我明白为什么我有这个问题。 I put the JavaFX content in a ScrollPane.
我将 JavaFX 内容放在 ScrollPane 中。 I did not think that it would be relevant in this case, but it is.
我不认为它在这种情况下是相关的,但它是相关的。 The code is:
代码是:
VBox vBox = new VBox(node);
vBox.setAlignment(Pos.CENTER);
Group group = new Group(vBox );
StackPane content = new StackPane(group);
group.layoutBoundsProperty().addListener((observable, oldBounds, newBounds) -> {
content.setMinWidth(newBounds.getWidth());
content.setMinHeight(newBounds.getHeight());
});
ScrollPane scrollPane = new ScrollPane(content);
scrollPane.setPannable(true);
scrollPane.setFitToHeight(true);
scrollPane.setFitToWidth(true);
scrollPane.setPrefSize(500, 500);
I used the following StackOverflow answer for the ScrollPane: JAVAFX zoom, scroll in ScrollPane我对 ScrollPane 使用了以下 StackOverflow 答案: JAVAFX zoom, scroll in ScrollPane
Perhaps there is a way to still be able to pan and zoom in the scrollPane but still seeing correctly the animations?也许有一种方法可以在 scrollPane 中平移和缩放,但仍然可以正确看到动画?
Here is a reproductible example showing everything:这是一个显示所有内容的可复制示例:
public class TestAnimationInScroll extends Application {
public static void main(String[] args) {
launch(args);
}
public void start(Stage primaryStage) {
Rectangle rect = new Rectangle(100, 100, 50, 50);
rect.setFill(Color.BLUE);
Group root = new Group();
root.getChildren().add(rect);
VBox vBox = new VBox(root);
vBox.setAlignment(Pos.CENTER);
Group group = new Group(root);
StackPane content = new StackPane(group);
group.layoutBoundsProperty().addListener((observable, oldBounds, newBounds) -> {
content.setMinWidth(newBounds.getWidth());
content.setMinHeight(newBounds.getHeight());
});
ScrollPane scrollPane = new ScrollPane(content);
scrollPane.setPannable(true);
scrollPane.setFitToHeight(true);
scrollPane.setFitToWidth(true);
scrollPane.setPrefSize(500, 500);
Timeline timeline = new Timeline();
KeyFrame kf0 = new KeyFrame(Duration.ZERO, new KeyValue(rect.xProperty(), 0));
KeyFrame kf1 = new KeyFrame(Duration.seconds(10), new KeyValue(rect.xProperty(), 100));
timeline.getKeyFrames().addAll(kf0, kf1);
timeline.play();
content.setOnScroll(new EventHandler<ScrollEvent>() {
public void handle(ScrollEvent event) {
double zoomFactor = 1.05;
double deltaY = event.getDeltaY();
if (deltaY < 0) {
zoomFactor = 1 / zoomFactor;
}
Bounds groupBounds = group.getBoundsInLocal();
final Bounds viewportBounds = scrollPane.getViewportBounds();
double valX = scrollPane.getHvalue() * (groupBounds.getWidth() - viewportBounds.getWidth());
double valY = scrollPane.getVvalue() * (groupBounds.getHeight() - viewportBounds.getHeight());
group.setScaleX(group.getScaleX() * zoomFactor);
group.setScaleY(group.getScaleY() * zoomFactor);
Point2D posInZoomTarget = group.parentToLocal(new Point2D(event.getX(), event.getY()));
Point2D adjustment = group.getLocalToParentTransform().deltaTransform(posInZoomTarget.multiply(zoomFactor - 1));
scrollPane.layout();
scrollPane.setViewportBounds(groupBounds);
groupBounds = group.getBoundsInLocal();
scrollPane.setHvalue((valX + adjustment.getX()) / (groupBounds.getWidth() - viewportBounds.getWidth()));
scrollPane.setVvalue((valY + adjustment.getY()) / (groupBounds.getHeight() - viewportBounds.getHeight()));
}
});
Scene scene = new Scene(scrollPane, 800, 600);
primaryStage.setScene(scene);
primaryStage.show();
}
}
As you can see, it is possible to zoom in the ScrollPane, but the effect of the animation is not visible.可以看到,ScrollPane可以放大,但是animation的效果是看不到的。
If I use a ScaleTransition in the same context, it works, such as:如果我在相同的上下文中使用 ScaleTransition ,它可以工作,例如:
public class TestAnimationInScroll extends Application {
public static void main(String[] args) {
launch(args);
}
public void start(Stage primaryStage) {
Rectangle rect = new Rectangle(100, 100, 50, 50);
rect.setFill(Color.BLUE);
Group root = new Group();
root.getChildren().add(rect);
VBox vBox = new VBox(root);
vBox.setAlignment(Pos.CENTER);
Group group = new Group(root);
StackPane content = new StackPane(group);
group.layoutBoundsProperty().addListener((observable, oldBounds, newBounds) -> {
content.setMinWidth(newBounds.getWidth());
content.setMinHeight(newBounds.getHeight());
});
ScrollPane scrollPane = new ScrollPane(content);
scrollPane.setPannable(true);
scrollPane.setFitToHeight(true);
scrollPane.setFitToWidth(true);
scrollPane.setPrefSize(500, 500);
ScaleTransition transition = new ScaleTransition(Duration.seconds(5), rect);
transition.setFromX(1d);
transition.setFromY(1d);
transition.setToX(3d);
transition.setToY(3d);
transition.play();
content.setOnScroll(new EventHandler<ScrollEvent>() {
public void handle(ScrollEvent event) {
double zoomFactor = 1.05;
double deltaY = event.getDeltaY();
if (deltaY < 0) {
zoomFactor = 1 / zoomFactor;
}
Bounds groupBounds = group.getBoundsInLocal();
final Bounds viewportBounds = scrollPane.getViewportBounds();
double valX = scrollPane.getHvalue() * (groupBounds.getWidth() - viewportBounds.getWidth());
double valY = scrollPane.getVvalue() * (groupBounds.getHeight() - viewportBounds.getHeight());
group.setScaleX(group.getScaleX() * zoomFactor);
group.setScaleY(group.getScaleY() * zoomFactor);
Point2D posInZoomTarget = group.parentToLocal(new Point2D(event.getX(), event.getY()));
Point2D adjustment = group.getLocalToParentTransform().deltaTransform(posInZoomTarget.multiply(zoomFactor - 1));
scrollPane.layout();
scrollPane.setViewportBounds(groupBounds);
groupBounds = group.getBoundsInLocal();
scrollPane.setHvalue((valX + adjustment.getX()) / (groupBounds.getWidth() - viewportBounds.getWidth()));
scrollPane.setVvalue((valY + adjustment.getY()) / (groupBounds.getHeight() - viewportBounds.getHeight()));
}
});
Scene scene = new Scene(scrollPane, 800, 600);
primaryStage.setScene(scene);
primaryStage.show();
}
}
I understood the problem: in fact it is because both the StackPane
and Group
will automatically update their layout when the children position or translation change.我理解这个问题:实际上是因为当子 position 或翻译发生变化时,
StackPane
和Group
都会自动更新它们的布局。 The solution is to not allow that.解决方案是不允许这样做。
It is simple for the Group
because it has a setAutoSizeChildren(boolean)
method. Group
很简单,因为它有一个setAutoSizeChildren(boolean)
方法。
But I had to create a subclass of StackPane
to do the same thing:但是我必须创建一个
StackPane
的子类来做同样的事情:
private class MyStackPane extends StackPane {
private boolean allowLayoutChildren = true;
private MyStackPane(Node root) {
super(root);
}
private void allowLayoutChildren(boolean allow) {
this.allowLayoutChildren = allow;
}
@Override
public void layoutChildren() {
if (allowLayoutChildren) {
super.layoutChildren();
}
}
}
Then the full (working) code is:然后完整的(工作)代码是:
public class TestAnimationInScroll extends Application {
private Group root = null;
public static void main(String[] args) {
launch(args);
}
@Override
public void start(Stage primaryStage) {
Rectangle rect = new Rectangle(100, 100, 50, 50);
rect.setFill(Color.BLUE);
root = new Group();
root.getChildren().add(rect);
MyStackPane content = new MyStackPane(root);
root.layoutBoundsProperty().addListener((observable, oldBounds, newBounds) -> {
content.setMinWidth(newBounds.getWidth());
content.setMinHeight(newBounds.getHeight());
});
ScrollPane scrollPane = new ScrollPane(content);
scrollPane.setPannable(true);
scrollPane.setFitToHeight(true);
scrollPane.setFitToWidth(true);
scrollPane.setPrefSize(500, 500);
root.setAutoSizeChildren(false);
content.allowLayoutChildren(false);
Timeline timeline = new Timeline();
KeyFrame kf0 = new KeyFrame(Duration.ZERO, new KeyValue(rect.xProperty(), 0));
KeyFrame kf1 = new KeyFrame(Duration.seconds(10), new KeyValue(rect.xProperty(), 100));
timeline.getKeyFrames().addAll(kf0, kf1);
timeline.play();
content.setOnScroll(new EventHandler<ScrollEvent>() {
@Override
public void handle(ScrollEvent event) {
double zoomFactor = 1.05;
double deltaY = event.getDeltaY();
if (deltaY < 0) {
zoomFactor = 1 / zoomFactor;
}
Bounds groupBounds = root.getBoundsInLocal();
final Bounds viewportBounds = scrollPane.getViewportBounds();
double valX = scrollPane.getHvalue() * (groupBounds.getWidth() - viewportBounds.getWidth());
double valY = scrollPane.getVvalue() * (groupBounds.getHeight() - viewportBounds.getHeight());
root.setScaleX(root.getScaleX() * zoomFactor);
root.setScaleY(root.getScaleY() * zoomFactor);
Point2D posInZoomTarget = root.parentToLocal(new Point2D(event.getX(), event.getY()));
Point2D adjustment = root.getLocalToParentTransform().deltaTransform(posInZoomTarget.multiply(zoomFactor - 1));
scrollPane.layout();
scrollPane.setViewportBounds(groupBounds);
groupBounds = root.getBoundsInLocal();
scrollPane.setHvalue((valX + adjustment.getX()) / (groupBounds.getWidth() - viewportBounds.getWidth()));
scrollPane.setVvalue((valY + adjustment.getY()) / (groupBounds.getHeight() - viewportBounds.getHeight()));
}
});
Scene scene = new Scene(scrollPane, 800, 600);
primaryStage.setScene(scene);
primaryStage.show();
}
private class MyStackPane extends StackPane {
private boolean allowLayoutChildren = true;
private MyStackPane(Node root) {
super(root);
}
private void allowLayoutChildren(boolean allow) {
this.allowLayoutChildren = allow;
}
@Override
public void layoutChildren() {
if (allowLayoutChildren) {
super.layoutChildren();
}
}
}
}
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.