JavaFX动画没有移动节点



我目前正在开发一个小型JavaFX库来显示svg内容。我正在制作动画。它们工作正常,除了x和y位置的animateanimateTransform,我不明白为什么。

当我这样做的时候,它完美地工作:

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();
}

}

但当我在图书馆里做我认为完全相同的事情时,矩形似乎有点抽搐,但并没有真正移动。

我的代码是:

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);

奇怪的是,当我对width或height属性做同样的事情时,它可以毫无问题地工作。

我怀疑我的场景创建不正确(我检查了我是否在Thread平台中完成了所有这些(,但其他一切都正常工作,没有任何问题。

如果我尝试使用TranslateTransition,我会遇到完全相同的问题。

在这里对这个问题发表了一些评论之后,我现在不明白为什么我会有这个问题(但不知道如何解决它,至少目前是这样(。我将JavaFX内容放在ScrollPane中。我不认为它会在这种情况下相关,但它是。代码是:

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);

我对ScrollPane使用了以下StackOverflow答案:JAVAFX缩放,在ScrollPane中滚动

这里有一个可复制的例子,展示了一切:

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();
}
}

如您所见,可以放大"滚动窗格",但动画效果不可见。

如果我在相同的上下文中使用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();
}
}

有人知道我的错误在哪里吗?也许有一种方法仍然可以在滚动窗格中平移和缩放,但仍然可以正确地看到动画?

我理解这个问题:事实上,这是因为StackPaneGroup都会在子级位置或翻译发生变化时自动更新其布局。解决办法是不允许这种情况发生。

它对于Group来说很简单,因为它有一个setAutoSizeChildren(boolean)方法。

但我不得不创建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();
}
}
}

那么完整的(工作(代码是:

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();
}
}
}
}

最新更新