java - 文字移动动画

标签 java animation javafx

我是 JavaFX 动画的新手,我了解它的工作原理的基本概念;您需要一个开始关键帧和一个结束关键帧,然后就可以播放它。

我想要做的是有一个字符串列表,例如“0, 1, 2, 3, 4, 5...10”,和一个变量,index。然后我想做的是在屏幕上同时显示 3 个这样的 3 个不同的 Text 对象:

0   1   2

在上面的示例中,index = 0。如果index = 1,它看起来像这样:

1   2   3

你明白了,所以我想做的是,每次 index 递增(它必须是一个 Property),就会有一个动画数字交易空间。

就像这样(符号数字代表褪色):

index = 0 : 0   1   2
index = 1 : )  1   2#
frame --- : ) 1   2 #
frame --- : )1   2  #
frame --- : 1   2   3

因此从理论上讲,存储这些数字的列表可能是无限的,因此我不能(不应该)为每个数字都有一个单独的 Text 对象。我无法弄清楚应该如何完成此操作,因为移动 Text 对象本身的关键帧会使事情变得复杂。

最佳答案

这是一个示例。

app

import javafx.animation.*;
import javafx.application.Application;
import javafx.beans.binding.StringBinding;
import javafx.beans.property.*;
import javafx.geometry.*;
import javafx.scene.Scene;
import javafx.scene.control.*;
import javafx.scene.layout.*;
import javafx.scene.paint.Color;
import javafx.stage.Stage;
import javafx.util.Duration;

import java.util.List;
import java.util.stream.*;

public class NumberSwitcher extends Application {

    private static final int NUM_NUMS_DISPLAYED = 3;
    private static final Duration TRANSITION_TIME = Duration.seconds(0.5);

    private final IntegerProperty idx   = new SimpleIntegerProperty(0);
    private final IntegerProperty toIdx = new SimpleIntegerProperty(0);

    private final List<Label> labels =
            IntStream.range(0, NUM_NUMS_DISPLAYED)
                    .mapToObj(
                            this::createLabel
                    )
                    .collect(Collectors.toList());

    private final Button incrementButton = new Button("Increment");

    @Override
    public void start(Stage stage) {
        stage.setScene(
                new Scene(
                        createLayout(),
                        Color.PALEGREEN
                )
        );
        stage.show();

        enableTransitions();
    }

    private StackPane createLayout() {
        incrementButton.setStyle("-fx-base: darkslateblue; -fx-text-fill: papayawhip; -fx-font-size: 20px;");

        HBox numbers = new HBox(10);
        numbers.getChildren().addAll(labels);
        numbers.setPadding(new Insets(15));
        numbers.setMaxWidth(HBox.USE_PREF_SIZE);

        VBox layout = new VBox(
                15,
                numbers,
                incrementButton
        );
        layout.setAlignment(Pos.CENTER);
        layout.setPadding(new Insets(15));
        layout.setStyle("-fx-background-color: null;");

        StackPane centered = new StackPane(layout);
        centered.setStyle("-fx-background-color: null;");
        return centered;
    }

    private Label createLabel(final int i) {
        Label label = new Label();
        label.setStyle(
                "-fx-font-size: 50px; -fx-font-family: monospace; -fx-text-fill: midnightblue;"
        );

        label.textProperty().bind(new StringBinding() {
            {
                super.bind(idx);
            }

            @Override
            protected String computeValue() {
                return "" + ((idx.get() + i) % 10);
            }
        });

        return label;
    }

    private void enableTransitions() {
        ParallelTransition changeNumbers = new ParallelTransition(
                createFadeFirst()
        );

        IntStream.range(1, labels.size())
                .mapToObj(this::createMoveLeft)
                .forEachOrdered(
                        moveLeft -> changeNumbers.getChildren().add(moveLeft)
                );

        changeNumbers.setOnFinished(e -> idx.set(toIdx.get()));

        toIdx.addListener((observable, oldValue, newValue) ->
                changeNumbers.play()
        );

        // You can disable incrementing while the transition is running,
        // but that is kind of annoying, so I chose not to.
//        incrementButton.disableProperty().bind(
//                changeNumbers.statusProperty().isEqualTo(
//                        PauseTransition.Status.RUNNING
//                )
//        );

        incrementButton.setOnAction(e -> {
            if (idx.get() != toIdx.get()) {
                idx.set(toIdx.get());
            }

            toIdx.set((toIdx.get() + 1) % 10);
        });
    }

    private FadeTransition createFadeFirst() {
        FadeTransition fadeFirst = new FadeTransition(
                TRANSITION_TIME,
                labels.get(0)
        );
        fadeFirst.setFromValue(1);
        fadeFirst.setToValue(0);
        fadeFirst.setOnFinished(e -> labels.get(0).setOpacity(1));
        return fadeFirst;
    }

    private TranslateTransition createMoveLeft(int i) {
        TranslateTransition moveLeft = new TranslateTransition(
                TRANSITION_TIME,
                labels.get(i)
        );
        double dx =
                labels.get(i).getBoundsInParent().getMinX()
                        - labels.get(i-1).getBoundsInParent().getMinX();

        moveLeft.setFromX(0);
        moveLeft.setToX(-dx);
        moveLeft.setOnFinished(e -> labels.get(i).setTranslateX(0));

        return moveLeft;
    }

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

}

其他问题的答案

I see you are translating the nodes, but, at what point do you change the location in the HBox?

此示例的 HBox 中的位置(例如,layoutX 和layoutY)在 HBox 中最初布局后就不会更改。 HBox是一个布局管理器,它会自动设置HBox中项目的layoutX和layoutY坐标。 HBox 布局算法将从左到右(默认情况下)按顺序将每个项目放置在其子列表中,并将每个项目的大小调整为其首选大小。 HBox 基于脏标志工作,因此它只会在需要时重新布局内部组件(例如组件更改或组件的 CSS 样式更改或 HBox 的可用区域更改),这些都不会发生在本例中样本。

Are you just translating them, switching the text, then resetting the translation?

是的。

Could you just give a quick overview of how this works?

三个标签被添加到 HBox。每个标签使用等宽字体和相同数量的字符,因此每个标签的宽度相等。记录两个指数。一个是文本区域中显示字符的当前索引,另一个是 toIdx,它是要移动到的文本数组中的下一个索引。提供了一个增量按钮,它将增加 toIdx。在 toIdx 上放置一个更改监听器,以便当它发生更改时,它会启动一组动画,第一个动画使第一个标签淡出,其他动画将剩余的标签移动到左侧的下一个位置。动画完成后,每个标签的平移设置为 0,因此每个移动的标签都会移回其原始位置。此外,动画完成后,当前索引将设置为 toIdx。当 toIdx 更新时,绑定(bind)用于自动更新每个标签中的文本。

效果是,当按下递增按钮时,第一个标签中的数字消失,右侧标签中的数字向左移动,一旦到达,就返回到原来的位置,但其值不变设置为下一个最大数字。这提供了问题中所要求的效果。

关于java - 文字移动动画,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/31352337/

相关文章:

java - Apache pig 脚本,错误 1070 : Java UDF could not resolve import

ios - 变换动画无法重复使用

eclipse - 未找到模块 java.xml.bind

css - 如何在 JavaFX 中使用后备字体?

JavaFX:点击某物时播放声音

java - 如何突出显示在javafx中使用正则表达式找到的单词

java - 更改 Java OpenGL 中的对象变量

java - 具有单个模式字母的 DateTimeFormatter 格式无法解析短月份名称

animation - AppDelegate 中的动画后不显示 Root View Controller

html - 如何循环播放该动画,V2