【问题标题】:JavaFX - using FillTransition and TranslateTransition misplaces the objectJavaFX - 使用 FillTransition 和 TranslateTransition 错位对象
【发布时间】:2015-04-10 17:43:42
【问题描述】:

所以我有几个不同大小的Rectangle 对象,名为r1r2,最初出现在屏幕的死角。它们有不同的颜色(比如说一个是红色的,另一个是蓝色的),较小的放在较大的上面,所以即使它们堆叠在中心,两者仍然可以区分。我想让他们四处走动,回到原来的位置,因此使用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);

然而,在他们的移动过程中,如果r1r2 中的任何一个被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);

通过将Rectangles 从Color.BLACK 转换为Color.BLACK 5 秒,它会产生按钮消失5 秒的效果。另外,我在r1r2 上有以下setOnMouseClicked,因此当用户输入时它们会消失:

r1.setOnMouseClicked((MouseEvent t) -> {
    ft1.play();
});
r2.setOnMouseClicked((MouseEvent t) -> {
    ft2.play();
});

在两个物体消失 5 秒后,它们必须重新出现在中心,就像它们在开始时所做的那样,并使用 tt1tt2 重复相同的摆动运动,这是我在打开 setOnFinished 时实现的ft1ft2:

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

然而,问题是,当r1r2重新出现时,它们不是位于中心,而是位于它们上次消失的位置——换句话说,它们重生的位置就是它们所在的位置。在检测到用户的MouseEvent 时,在上一次TranslateTransition 期间。我试图通过使用r1.setX(centerX)r1.setY(centerY) 来修改它,其中centerXcenterY 是一开始使用的原始中心坐标,但它无法解决问题。事实上,当我使用r1.getX() 时,返回值等于原始centerX 值,即使r1 没有放在中心很明显。这让我怀疑TranslateTransition 在不改变实际getX() 值的情况下履行其职责。我也想过在TranslateTransitionFillTransition上使用ParallelTransition,所以tt1可以在ft1生效时完成,但从那时起ft1将在tt1已经运行时开始运行有时,它不会提供可行的解决方案。

所以我的问题是,如果一个对象的TranslateTransition在中间被打断,我如何恢复对象的“原始”坐标,而不是TranslateTransition被打断时对象上次离开的位置?

附言我想避免在每次检测到MouseEvent 时创建新的Rectangle 对象,因为这意味着所有链接到r1r2TranslateTransitionFillTransition 也必须重新创建。

【问题讨论】:

    标签: javafx transition


    【解决方案1】:

    示例解决方案

    运行程序,矩形将开始移动。单击矩形,它会立即消失。消失后不久,矩形将重新出现在其原始起始位置并开始沿其原始轨迹移动。

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

    这只是解决了一个矩形的问题。您可以将解决方案粘贴在一个循环中以处理多个矩形。

    观察

    1. 使用PauseTransition 而不是FillTransition,并且在暂停运行时节点的可见性设置为false。使用 FillTransition,用户仍然可以单击节点,即使用背景颜色填充的节点无法与背景明显区分开来。所以 FillTransition 可能是不可取的。
    2. fromX/fromY 属性是为翻译转换设置的,否则如果你停止并从头开始运行它,它将只使用节点的当前 translateX/translateY 值,而不是你想要的 0/0 的原始值.
    3. 虽然节点不可见,但无需继续运行 TranslateTransition,因此过渡会在这段时间内停止。
    4. 当平移过渡停止时,它会将 translateX/translateY 坐标留在当前为最后一个动画帧设置的位置。因此添加了将 translateX/translateY 设置为 0/0 的手动调用。
    5. 暂停过渡完成后,系统会请求从头开始播放翻译过渡,而不是之前暂停或停止的位置。

    了解转换

    节点在屏幕上的位置基于应用于其布局位置的变换。布局位置在节点的 layoutX/layoutY 属性中维护。但是,如果您对节点应用平移转换(正如您在 TranslateTransition 中隐式执行的操作),则该节点的屏幕位置将是 layoutX+translateX / layoutY+translateY

    背景研究

    1. 阅读Node documentation,尤其是关于变换和边界矩形的部分。
    2. 试试这个layout bounds demonstration 来帮助理解这些概念。

    所以关键是使用 setFromX 和 setFromY 从原始坐标开始。

    将 translateX/translateY 值手动设置为 0/0 也是关键。如果不这样做,节点将在其最后一个平移位置闪烁,然后开始从原点位置移动。我认为这是因为从您请求动画开始到它实际开始并使用 0/0 的 fromX/fromY 坐标之间存在帧延迟。这是一种奇怪的行为。

    【讨论】:

    • 这正是我想要的。所以关键是使用setFromXsetFromY从原始坐标开始。是的,我选择FillTransition 正是因为您指定的原因 - 按钮仍然可见,您的解决方案解决了问题。为了防止在黑色时进行进一步的用户输入,我使用了一个布尔标志,该标志在消失/重新出现之前被取消设置/设置。感谢您精心设计的解决方案。
    • 设置 fromX/fromY 是解决方案的一部分,但将 translateX/translateY 设置为 0/0 也很重要,如答案更新中所述。
    【解决方案2】:

    这对我来说很好。希望这是一个好的方法。可能有新的类来创建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);
        }
    
    }
    

    【讨论】:

      猜你喜欢
      • 2016-07-12
      • 1970-01-01
      • 2015-08-12
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多