在时间轴中同时播放的 JavaFX 动画

JavaFX animations playing simultaneously in timeline

提问人:Bursh 提问时间:11/14/2023 更新时间:11/15/2023 访问量:67

问:

我是 JavaFX 的新手。我正在尝试制作这个时间线,其中每个关键帧都发生在前一个关键帧完成之后。到目前为止,每个步骤都像我想要的那样一个接一个地发生。但是,一旦我播放动画,rotateBack 关键帧就会开始旋转我的对象。关于如何使 rotateBack 仅在 moveToRight 之后播放的任何提示?另外,我希望 rotateBack 在 moveToTop 播放之前完成其旋转。谢谢。

Pane selectedPane = select_pane.getValue();
                    
    double rootWidth = root_pane.getWidth();
    double rootHeight = root_pane.getHeight();
    double sixthWidth = rootWidth / 6;

    KeyValue moveTopLeftX = new KeyValue(selectedPane.layoutXProperty(), 0);
    KeyValue moveDownY = new KeyValue(selectedPane.layoutYProperty(), root_pane.getHeight()                   -    selectedPane.getHeight());

    KeyValue moveRightX = new KeyValue(selectedPane.layoutXProperty(), sixthWidth);
    KeyValue rotate180 = new KeyValue(selectedPane.rotateProperty(), 180);
    KeyValue moveToTopY = new KeyValue(selectedPane.layoutYProperty(), 0);

    KeyFrame startFrame = new KeyFrame(Duration.seconds(3), moveTopLeftX, moveToTopY);
    KeyFrame moveToRight = new KeyFrame(Duration.seconds(2), moveRightX);
    KeyFrame rotateBack = new KeyFrame(Duration.seconds(3), rotate180);
    KeyFrame moveToBottom = new KeyFrame(Duration.seconds(6), moveDownY);
    KeyFrame moveToTop = new KeyFrame(Duration.seconds(6), moveToTopY);

    Timeline timeline = new Timeline(startFrame, moveToBottom, moveToRight, rotateBack, moveToTop);

    timeline.play();

到目前为止,我尝试的内容显示在上面的代码中。任何提示非常感谢。

Java 动画 JavaFX 时间轴

评论

0赞 James_D 11/15/2023
您还可以添加其他 s。例如,将使旋转从 2 秒开始。KeyFramenew KeyFrame(Duration.seconds(2), new KeyValue(selectedPane.rotateProperty(), selectedPane.getRotate()))

答:

1赞 Thomas Liné 11/14/2023 #1

要仅在关键帧完成后播放关键帧,您可以创建不同的时间轴,并在 timeline.setOnFinished() 函数中播放下一个关键帧。
对于您的示例,它看起来像这样:

Pane selectedPane = select_pane.getValue();
                
double rootWidth = root_pane.getWidth();
double rootHeight = root_pane.getHeight();
double sixthWidth = rootWidth / 6;

KeyValue moveTopLeftX = new KeyValue(selectedPane.layoutXProperty(), 0);
KeyValue moveDownY = new KeyValue(selectedPane.layoutYProperty(), root_pane.getHeight()                   -    selectedPane.getHeight());

KeyValue moveRightX = new KeyValue(selectedPane.layoutXProperty(), sixthWidth);
KeyValue rotate180 = new KeyValue(selectedPane.rotateProperty(), 180);
KeyValue moveToTopY = new KeyValue(selectedPane.layoutYProperty(), 0);

KeyFrame startFrame = new KeyFrame(Duration.seconds(3), moveTopLeftX, moveToTopY);
KeyFrame moveToRight = new KeyFrame(Duration.seconds(2), moveRightX);
KeyFrame rotateBack = new KeyFrame(Duration.seconds(3), rotate180);
KeyFrame moveToBottom = new KeyFrame(Duration.seconds(6), moveDownY);
KeyFrame moveToTop = new KeyFrame(Duration.seconds(6), moveToTopY);

Timeline timelineStart = new Timeline(startFrame, moveToBottom, moveToRight);

Timeline timelineRotateBack = new Timeline(rotateBack);

Timeline timelineMoveToTop = new Timeline(moveToTop);

timelineStart.setOnFinished(new EventHandler<ActionEvent>() {
            @Override
            public void handle(ActionEvent event) {
                timelineRotateBack.play();
            }
        });

timelineRotateBack.setOnFinished(new EventHandler<ActionEvent>() {
            @Override
            public void handle(ActionEvent event) {
                timelineMoveToTop.play();
            }
        });

timelineStart.play();

在这里,它将首先播放所有这些关键帧(startFrame、moveToBottom、moveToRight),当所有动画完成后,它将播放 rotateBack 关键帧,当这个动画完成时,它将播放 moveToTop

1赞 jewelsea 11/15/2023 #2

过渡非常适合这个问题

本文提供了一个基于 SequentialTransition 的示例,而不是 .您可以改用 a,如其他评论或答案中所述。但是,对于您描述的功能,使用过渡似乎更合适。TimelineTimeline

该示例对各种类型的事务进行排序,并执行所需的动画。TranslateTransitionRotateTransition

如果您希望并行执行序列的某些部分,则可以对这些部分使用 a(如果需要,可以合并并行和顺序转换)。ParallelTransition

使用动画的平移属性

执行动画时应使用 translateX/Y/Z 属性,而不是 layoutX/Y 属性。虽然您可以对布局更改进行动画处理,但这是一个高级主题,不建议刚接触 JavaFX 的人使用。此处提供的示例基于翻译属性,而不是布局属性。

选择合适的插值器

关键帧的默认插值器为 LINEAR,过渡的默认插值器为 EASE_BOTH。通过在关键帧或转场上设置插值器,选择适合您应用的插值器。对于这个例子,我觉得过渡的默认插值是最适合的,从而产生了更平滑的动画感觉。EASE_BOTH

示例代码

import javafx.animation.*;
import javafx.application.Application;
import javafx.scene.*;
import javafx.scene.layout.*;
import javafx.scene.paint.Color;
import javafx.scene.shape.Rectangle;
import javafx.stage.Stage;
import javafx.util.Duration;

import java.io.IOException;

public class TransitionApp extends Application {
    private static final Color INDIA_INK = Color.web("#3d3f4a");
    private static final double W = 400, H = 400;
    private static final double BOX_W = 20, BOX_H = 20;
    private static final double BOX_TOP_SIDE_INDICATOR_H = 3;
    private static final double MOVE_STEP = 50;
    private static final Duration TIME_STEP = Duration.seconds(3);

    public void start(Stage stage) throws IOException {
        Group box = createBox(BOX_W, BOX_H, BOX_TOP_SIDE_INDICATOR_H);
        box.setTranslateX(W / 2 - BOX_W / 2);
        box.setTranslateY(H / 2 - BOX_H / 2);

        Pane root = new Pane(box);
        root.setBackground(Background.fill(INDIA_INK));
        root.setPrefSize(W, H);

        stage.setResizable(false);
        stage.setScene(new Scene(root));
        stage.show();

        SequentialTransition transition = createTransition(box);
        transition.play();
    }

    private SequentialTransition createTransition(Group box) {
        TranslateTransition moveToTopLeft = new TranslateTransition(TIME_STEP);
        moveToTopLeft.setToX(0);
        moveToTopLeft.setToY(0);

        TranslateTransition moveToBottom = new TranslateTransition(TIME_STEP.multiply(2));
        moveToBottom.setToY(H - BOX_H);

        TranslateTransition moveRight = new TranslateTransition(TIME_STEP);
        moveRight.setToX(MOVE_STEP);

        RotateTransition rotate180 = new RotateTransition(TIME_STEP);
        rotate180.setByAngle(180);

        TranslateTransition moveToTop = new TranslateTransition(TIME_STEP.multiply(2));
        moveToTop.setToY(0);

        SequentialTransition sequentialTransition = new SequentialTransition(
                box,
                moveToTopLeft, moveToBottom, moveRight, rotate180, moveToTop
        );
        sequentialTransition.setAutoReverse(true);
        sequentialTransition.setCycleCount(Transition.INDEFINITE);
        
        return sequentialTransition;
    }

    private static Group createBox(double w, double h, double topSideIndicatorH) {
        Rectangle rectangle = new Rectangle(
                w, h, Color.GREEN
        );

        Rectangle topLine = new Rectangle(
                w, topSideIndicatorH, Color.RED
        );

        return new Group(
                rectangle, topLine
        );
    }

    public static void main(String[] args) {
        launch(args);
    }
}