JavaFX - 使用 FillTransition 和 TranslateTransition 放错了对象
JavaFX - using FillTransition and TranslateTransition misplaces the object
所以我有几个 Rectangle
个不同大小的对象,分别命名为 r1
和 r2
,它们最初出现在屏幕的正中央。它们有不同的颜色(比如一个是红色的,另一个是蓝色的),较小的放在较大的上面,所以即使它们堆叠在中间,两者仍然可以区分。我希望他们四处移动 return 到他们原来的位置,因此使用 TranslateTransition
如下:
tt1 = new TranslateTransition(Duration.millis(20000), button1);
tt1.setByX(100); // moves 100 pixels to the right
tt1.setByY(-100); // moves 100 pixels down
tt1.setCycleCount(20); // oscillates 20 times
tt1.setAutoReverse(true);
tt2 = new TranslateTransition(Duration.millis(20000), button2);
tt2.setByX(-100); // moves 100 pixels to the left
tt2.setByY(100); // moves 100 pixels up
tt2.setCycleCount(20); // oscillates 20 times
tt2.setAutoReverse(true);
然而,在他们移动的过程中,如果 r1
和 r2
中的任何一个被 MouseEvent
按下,我希望他们消失几秒钟(比如 5 秒)并再次活着回来。使用背景颜色是完全黑色的事实,我使用 FillTransition
来实现该效果:
FillTransition ft1 = new FillTransition(Duration.millis(5000), r1, Color.BLACK, Color.BLACK);
FillTransition ft2 = new FillTransition(Duration.millis(5000), r2, Color.BLACK, Color.BLACK);
通过将Rectangle
s从Color.BLACK
转换为Color.BLACK
5秒,它给出了按钮消失5秒的效果。此外,我在 r1
和 r2
上有以下 setOnMouseClicked
,因此它们可以在用户输入时消失:
r1.setOnMouseClicked((MouseEvent t) -> {
ft1.play();
});
r2.setOnMouseClicked((MouseEvent t) -> {
ft2.play();
});
两个物体消失 5 秒后,它们必须重新出现在中心,就像开始时一样,并使用 tt1
和 tt2
重复相同的摆动动作,我在 ft1
和 ft2
:
上实现了 setOnFinished
ft1.setOnFinished((ActionEvent event) -> {
r1.setFill(color1); // restore the original color
tt1.play();
});
ft2.setOnFinished((ActionEvent event) -> {
r2.setFill(color2); // restore the original color
tt2.play();
});
然而问题是,当r1
和r2
再次出现时,它们的位置不是在中心,而是在它们最后消失的位置——换句话说,位置他们重生的起点是上次 TranslateTransition
检测到用户的 MouseEvent
时他们所在的位置。我试过用r1.setX(centerX)
和r1.setY(centerY)
来修改,其中centerX
和centerY
是一开始使用的原始中心坐标,但无法解决问题。事实上,当我使用 r1.getX()
时,returned 值等于原始 centerX
值,即使很明显 r1
没有放在中间。这让我怀疑 TranslateTransition
在不改变实际 getX()
值的情况下履行其职责。我还想过在TranslateTransition
和FillTransition
上以某种方式使用ParallelTransition
,所以tt1
可以在ft1
生效时完成,但从那以后ft1
当 tt1
已经 运行 一段时间后,将开始 运行,它不会提供可行的解决方案。
所以我的问题是,如果一个对象的 TranslateTransition
在中间被打断了,我该如何恢复对象的 "original" 坐标,而不是 [=17= 时对象最后停止的位置]被打断了?
p.s。我想避免在每次检测到 MouseEvent
时创建新的 Rectangle
对象,因为这意味着所有 TranslateTransition
和 FillTransition
链接到 r1
和 r2
也必须重新创建。
示例解决方案
运行程序,矩形开始移动。单击矩形,它会立即消失。在它消失后不久,矩形会重新出现在它原来的起始位置,并开始沿着它原来的轨迹移动。
import javafx.animation.*;
import javafx.application.Application;
import javafx.scene.*;
import javafx.scene.shape.Rectangle;
import javafx.stage.Stage;
import javafx.util.Duration;
public class Pauser extends Application {
@Override
public void start(Stage stage) throws Exception {
final Rectangle r1 = new Rectangle(
50, 150, 30, 30
);
final TranslateTransition tt1 = new TranslateTransition(
Duration.seconds(5),
r1
);
tt1.setFromX(0); // start at the layout origin for the node
tt1.setFromY(0); //
tt1.setByX(100); // moves 100 pixels to the right
tt1.setByY(-100); // moves 100 pixels down
tt1.setCycleCount(TranslateTransition.INDEFINITE);
tt1.setAutoReverse(true);
tt1.play();
final PauseTransition pt1 = new PauseTransition(
Duration.seconds(1)
);
pt1.setOnFinished(event -> {
tt1.playFromStart();
r1.setVisible(true);
});
r1.setOnMouseClicked(event -> {
r1.setVisible(false);
tt1.stop();
r1.setTranslateX(0);
r1.setTranslateY(0);
pt1.play();
});
stage.setScene(
new Scene(
new Group(r1),
200, 200
)
);
stage.show();
}
public static void main(String[] args) {
launch(args);
}
}
这只解决了一个矩形的问题。您可以将解决方案放在一个循环中以处理多个矩形。
观察结果
- A PauseTransition is used rather than a FillTransition 并且节点的可见性设置为 false 而暂停是 运行ning。使用 FillTransition,用户仍然可以单击节点,即使无法从背景中明显地区分填充有背景色的节点。所以 FillTransition 可能是不可取的。
- fromX/fromY 属性是为转换转换设置的,否则如果你停止并从头开始 运行 它只会使用节点的当前 translateX/translateY 值而不是原点0/0 的值就是你想要的。
- 虽然节点不可见,但无需保持 运行 TranslateTransition,因此在此期间停止转换。
- 当平移转换停止时,它会将 translateX/translateY 坐标留在当前为最后一个动画帧设置的位置。因此添加了将 translateX/translateY 设置为 0/0 的手动调用。
- 暂停过渡完成后,请求从头开始播放翻译过渡,而不是之前暂停或停止的地方。
了解转换
节点在屏幕上的位置基于应用于其布局位置的变换。布局位置在节点的 layoutX/layoutY 属性中维护。但是,如果您对节点应用平移转换(正如您在 TranslateTransition 中隐式执行的那样),则节点的屏幕位置将为 layoutX+translateX / layoutY+translateY。
背景研究
- 阅读 Node documentation,尤其是关于变换和边界矩形的部分。
- 试试这个 layout bounds demonstration 来帮助理解这些概念。
So the key was using setFromX and setFromY to start from the original coordinate.
手动设置 translateX/translateY 值为 0/0 也是关键。如果不这样做,节点将在开始从原点位置移动之前在其最后转换的位置闪烁。我认为这是因为从您请求动画开始到它实际开始并使用 0/0 的 fromX/fromY 坐标之间存在帧延迟。这是一种奇怪的行为。
这对我来说很好用。希望它是个好方法。可能有新的 class 来创建 extends rectangle
,其中包含事件和转换。这只是两个 rectangles
的样本。
import javafx.animation.FadeTransition;
import javafx.animation.TranslateTransition;
import javafx.application.Application;
import javafx.event.ActionEvent;
import javafx.event.EventHandler;
import javafx.geometry.Pos;
import javafx.scene.Scene;
import javafx.scene.input.MouseEvent;
import javafx.scene.layout.StackPane;
import javafx.scene.paint.Color;
import javafx.scene.shape.Rectangle;
import javafx.stage.Stage;
import javafx.util.Duration;
public class Main extends Application {
@Override
public void start(Stage primaryStage) throws Exception {
Rectangle rect1 = new Rectangle (100, 40, 120, 120);
rect1.setArcHeight(42);
rect1.setArcWidth(42);
rect1.setFill(Color.AQUA);
TranslateTransition tt1;
tt1 = new TranslateTransition(Duration.millis(3000), rect1);
tt1.setByX(200);
tt1.setByY(-200);
tt1.setCycleCount(2);
tt1.setAutoReverse(true);
tt1.play();
rect1.setOnMousePressed(new EventHandler<MouseEvent>() {
@Override public void handle(MouseEvent event) {
FadeTransition ft = new FadeTransition(Duration.millis(1000), rect1);
ft.setFromValue(1.0);
ft.setToValue(0.0);
ft.play();
event.consume();
}
});
tt1.setOnFinished(new EventHandler<ActionEvent>() {
@Override
public void handle(ActionEvent event) {
tt1.play();
FadeTransition ft = new FadeTransition(Duration.millis(1000), rect1);
ft.setToValue(1.0);
ft.play();
}
});
Rectangle rect2 = new Rectangle (100, 40, 80, 80);
rect2.setArcHeight(42);
rect2.setArcWidth(42);
rect2.setFill(Color.YELLOWGREEN);
TranslateTransition tt2;
tt2= new TranslateTransition(Duration.millis(6000), rect2);
tt2.setByX(-200);
tt2.setByY(200);
tt2.setCycleCount(2);
tt2.setAutoReverse(true);
tt2.play();
rect2.setOnMousePressed(new EventHandler<MouseEvent>() {
@Override public void handle(MouseEvent event) {
FadeTransition ft = new FadeTransition(Duration.millis(1000), rect2);
ft.setFromValue(1.0);
ft.setToValue(0.0);
ft.play();
event.consume();
}
});
tt2.setOnFinished(new EventHandler<ActionEvent>() {
@Override
public void handle(ActionEvent event) {
tt2.play();
FadeTransition ft = new FadeTransition(Duration.millis(1000), rect2);
ft.setToValue(1.0);
ft.play();
}
});
StackPane pane = new StackPane();
pane.setAlignment(Pos.CENTER);
pane.getChildren().addAll(rect1,rect2);
Scene scene = new Scene(pane, 600, 600);
primaryStage.setScene(scene);
primaryStage.show();
}
public static void main(String[] args){
launch(args);
}
}
所以我有几个 Rectangle
个不同大小的对象,分别命名为 r1
和 r2
,它们最初出现在屏幕的正中央。它们有不同的颜色(比如一个是红色的,另一个是蓝色的),较小的放在较大的上面,所以即使它们堆叠在中间,两者仍然可以区分。我希望他们四处移动 return 到他们原来的位置,因此使用 TranslateTransition
如下:
tt1 = new TranslateTransition(Duration.millis(20000), button1);
tt1.setByX(100); // moves 100 pixels to the right
tt1.setByY(-100); // moves 100 pixels down
tt1.setCycleCount(20); // oscillates 20 times
tt1.setAutoReverse(true);
tt2 = new TranslateTransition(Duration.millis(20000), button2);
tt2.setByX(-100); // moves 100 pixels to the left
tt2.setByY(100); // moves 100 pixels up
tt2.setCycleCount(20); // oscillates 20 times
tt2.setAutoReverse(true);
然而,在他们移动的过程中,如果 r1
和 r2
中的任何一个被 MouseEvent
按下,我希望他们消失几秒钟(比如 5 秒)并再次活着回来。使用背景颜色是完全黑色的事实,我使用 FillTransition
来实现该效果:
FillTransition ft1 = new FillTransition(Duration.millis(5000), r1, Color.BLACK, Color.BLACK);
FillTransition ft2 = new FillTransition(Duration.millis(5000), r2, Color.BLACK, Color.BLACK);
通过将Rectangle
s从Color.BLACK
转换为Color.BLACK
5秒,它给出了按钮消失5秒的效果。此外,我在 r1
和 r2
上有以下 setOnMouseClicked
,因此它们可以在用户输入时消失:
r1.setOnMouseClicked((MouseEvent t) -> {
ft1.play();
});
r2.setOnMouseClicked((MouseEvent t) -> {
ft2.play();
});
两个物体消失 5 秒后,它们必须重新出现在中心,就像开始时一样,并使用 tt1
和 tt2
重复相同的摆动动作,我在 ft1
和 ft2
:
setOnFinished
ft1.setOnFinished((ActionEvent event) -> {
r1.setFill(color1); // restore the original color
tt1.play();
});
ft2.setOnFinished((ActionEvent event) -> {
r2.setFill(color2); // restore the original color
tt2.play();
});
然而问题是,当r1
和r2
再次出现时,它们的位置不是在中心,而是在它们最后消失的位置——换句话说,位置他们重生的起点是上次 TranslateTransition
检测到用户的 MouseEvent
时他们所在的位置。我试过用r1.setX(centerX)
和r1.setY(centerY)
来修改,其中centerX
和centerY
是一开始使用的原始中心坐标,但无法解决问题。事实上,当我使用 r1.getX()
时,returned 值等于原始 centerX
值,即使很明显 r1
没有放在中间。这让我怀疑 TranslateTransition
在不改变实际 getX()
值的情况下履行其职责。我还想过在TranslateTransition
和FillTransition
上以某种方式使用ParallelTransition
,所以tt1
可以在ft1
生效时完成,但从那以后ft1
当 tt1
已经 运行 一段时间后,将开始 运行,它不会提供可行的解决方案。
所以我的问题是,如果一个对象的 TranslateTransition
在中间被打断了,我该如何恢复对象的 "original" 坐标,而不是 [=17= 时对象最后停止的位置]被打断了?
p.s。我想避免在每次检测到 MouseEvent
时创建新的 Rectangle
对象,因为这意味着所有 TranslateTransition
和 FillTransition
链接到 r1
和 r2
也必须重新创建。
示例解决方案
运行程序,矩形开始移动。单击矩形,它会立即消失。在它消失后不久,矩形会重新出现在它原来的起始位置,并开始沿着它原来的轨迹移动。
import javafx.animation.*;
import javafx.application.Application;
import javafx.scene.*;
import javafx.scene.shape.Rectangle;
import javafx.stage.Stage;
import javafx.util.Duration;
public class Pauser extends Application {
@Override
public void start(Stage stage) throws Exception {
final Rectangle r1 = new Rectangle(
50, 150, 30, 30
);
final TranslateTransition tt1 = new TranslateTransition(
Duration.seconds(5),
r1
);
tt1.setFromX(0); // start at the layout origin for the node
tt1.setFromY(0); //
tt1.setByX(100); // moves 100 pixels to the right
tt1.setByY(-100); // moves 100 pixels down
tt1.setCycleCount(TranslateTransition.INDEFINITE);
tt1.setAutoReverse(true);
tt1.play();
final PauseTransition pt1 = new PauseTransition(
Duration.seconds(1)
);
pt1.setOnFinished(event -> {
tt1.playFromStart();
r1.setVisible(true);
});
r1.setOnMouseClicked(event -> {
r1.setVisible(false);
tt1.stop();
r1.setTranslateX(0);
r1.setTranslateY(0);
pt1.play();
});
stage.setScene(
new Scene(
new Group(r1),
200, 200
)
);
stage.show();
}
public static void main(String[] args) {
launch(args);
}
}
这只解决了一个矩形的问题。您可以将解决方案放在一个循环中以处理多个矩形。
观察结果
- A PauseTransition is used rather than a FillTransition 并且节点的可见性设置为 false 而暂停是 运行ning。使用 FillTransition,用户仍然可以单击节点,即使无法从背景中明显地区分填充有背景色的节点。所以 FillTransition 可能是不可取的。
- fromX/fromY 属性是为转换转换设置的,否则如果你停止并从头开始 运行 它只会使用节点的当前 translateX/translateY 值而不是原点0/0 的值就是你想要的。
- 虽然节点不可见,但无需保持 运行 TranslateTransition,因此在此期间停止转换。
- 当平移转换停止时,它会将 translateX/translateY 坐标留在当前为最后一个动画帧设置的位置。因此添加了将 translateX/translateY 设置为 0/0 的手动调用。
- 暂停过渡完成后,请求从头开始播放翻译过渡,而不是之前暂停或停止的地方。
了解转换
节点在屏幕上的位置基于应用于其布局位置的变换。布局位置在节点的 layoutX/layoutY 属性中维护。但是,如果您对节点应用平移转换(正如您在 TranslateTransition 中隐式执行的那样),则节点的屏幕位置将为 layoutX+translateX / layoutY+translateY。
背景研究
- 阅读 Node documentation,尤其是关于变换和边界矩形的部分。
- 试试这个 layout bounds demonstration 来帮助理解这些概念。
So the key was using setFromX and setFromY to start from the original coordinate.
手动设置 translateX/translateY 值为 0/0 也是关键。如果不这样做,节点将在开始从原点位置移动之前在其最后转换的位置闪烁。我认为这是因为从您请求动画开始到它实际开始并使用 0/0 的 fromX/fromY 坐标之间存在帧延迟。这是一种奇怪的行为。
这对我来说很好用。希望它是个好方法。可能有新的 class 来创建 extends rectangle
,其中包含事件和转换。这只是两个 rectangles
的样本。
import javafx.animation.FadeTransition;
import javafx.animation.TranslateTransition;
import javafx.application.Application;
import javafx.event.ActionEvent;
import javafx.event.EventHandler;
import javafx.geometry.Pos;
import javafx.scene.Scene;
import javafx.scene.input.MouseEvent;
import javafx.scene.layout.StackPane;
import javafx.scene.paint.Color;
import javafx.scene.shape.Rectangle;
import javafx.stage.Stage;
import javafx.util.Duration;
public class Main extends Application {
@Override
public void start(Stage primaryStage) throws Exception {
Rectangle rect1 = new Rectangle (100, 40, 120, 120);
rect1.setArcHeight(42);
rect1.setArcWidth(42);
rect1.setFill(Color.AQUA);
TranslateTransition tt1;
tt1 = new TranslateTransition(Duration.millis(3000), rect1);
tt1.setByX(200);
tt1.setByY(-200);
tt1.setCycleCount(2);
tt1.setAutoReverse(true);
tt1.play();
rect1.setOnMousePressed(new EventHandler<MouseEvent>() {
@Override public void handle(MouseEvent event) {
FadeTransition ft = new FadeTransition(Duration.millis(1000), rect1);
ft.setFromValue(1.0);
ft.setToValue(0.0);
ft.play();
event.consume();
}
});
tt1.setOnFinished(new EventHandler<ActionEvent>() {
@Override
public void handle(ActionEvent event) {
tt1.play();
FadeTransition ft = new FadeTransition(Duration.millis(1000), rect1);
ft.setToValue(1.0);
ft.play();
}
});
Rectangle rect2 = new Rectangle (100, 40, 80, 80);
rect2.setArcHeight(42);
rect2.setArcWidth(42);
rect2.setFill(Color.YELLOWGREEN);
TranslateTransition tt2;
tt2= new TranslateTransition(Duration.millis(6000), rect2);
tt2.setByX(-200);
tt2.setByY(200);
tt2.setCycleCount(2);
tt2.setAutoReverse(true);
tt2.play();
rect2.setOnMousePressed(new EventHandler<MouseEvent>() {
@Override public void handle(MouseEvent event) {
FadeTransition ft = new FadeTransition(Duration.millis(1000), rect2);
ft.setFromValue(1.0);
ft.setToValue(0.0);
ft.play();
event.consume();
}
});
tt2.setOnFinished(new EventHandler<ActionEvent>() {
@Override
public void handle(ActionEvent event) {
tt2.play();
FadeTransition ft = new FadeTransition(Duration.millis(1000), rect2);
ft.setToValue(1.0);
ft.play();
}
});
StackPane pane = new StackPane();
pane.setAlignment(Pos.CENTER);
pane.getChildren().addAll(rect1,rect2);
Scene scene = new Scene(pane, 600, 600);
primaryStage.setScene(scene);
primaryStage.show();
}
public static void main(String[] args){
launch(args);
}
}