JavaFX - 使用FillTransition和TranslateTransition错位对象

时间:2015-04-10 07:40:54

标签: javafx transition

所以我有几个不同大小的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);

在移动过程中,如果r1按下r2MouseEvent中的任何一个,我希望它们消失几秒钟(比如说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);

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

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

两个物体消失5秒后,它们必须重新出现在中心,就像它们在开始时一样,并使用tt1tt2重复相同的摆动动作setOnFinishedft1上的ft2

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重新出现时,它们不是位于中心,而是位于它们最后消失的位置 - 换句话说,它们的位置重生是他们在检测到用户TranslateTransition的最后MouseEvent期间所处的位置。我尝试使用r1.setX(centerX)r1.setY(centerY)对其进行修改,其中centerXcenterY是开头使用的原始中心坐标,但无法解决问题。实际上,当我使用r1.getX()时,返回的值等于原始centerX值,即使显而易见r1未置于中心位置也是如此。这让我怀疑TranslateTransition在不改变实际getX()值的情况下履行职责。我还想过在ParallelTransitionTranslateTransition上以某种方式使用FillTransition,因此tt1可以在ft1生效时完成,但从那时起ft1 <{1}}已经运行一段时间后会开始运行,它不会提供可行的解决方案。

所以我的问题是,如果某个对象tt1在中间被中断,我该如何恢复该对象&#34;原始&#34;坐标,而不是TranslateTransition被中断时对象最后被遗弃的位置?

P.S。我希望每次检测到TranslateTransition时都避免创建新的Rectangle个对象,因为这意味着所有MouseEventTranslateTransition都与FillTransition和{{1}相关联必须重新创建。

2 个答案:

答案 0 :(得分:1)

示例解决方案

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

sample

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. 暂停转换完成后,会发出请求,要求从开始播放转换过渡,而不是先前暂停或停止的位置。
  6. 了解转化

    屏幕上节点的位置基于应用于其布局位置的变换。布局位置保留在节点的layoutX / layoutY属性中。但是,如果您将转换转换应用于节点(正如您在TranslateTransition中隐式执行的那样),那么节点的屏幕位置将为layoutX + translateX / layoutY + translateY

    背景研究

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

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

      将translateX / translateY值手动设置为0/0也是关键。如果没有这样做,节点将在开始从原点位置移动之前在其最后翻译位置闪烁。我认为这是因为从请求动画开始到实际开始并使用0/0的fromX / fromY坐标时会出现帧延迟。这是一种奇怪的行为。

答案 1 :(得分:0)

这对我来说很好。希望它是好方法。可能有新的类可以创建包含事件和转换的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);
    }

}