【问题标题】:Text moving animation文字移动动画
【发布时间】:2015-07-11 00:52:09
【问题描述】:

我是 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 对象的关键帧本身会使事情复杂化。

【问题讨论】:

    标签: java animation javafx


    【解决方案1】:

    这是一个示例。

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

    其他问题的答案

    我看到您正在翻译节点,但是,您在什么时候更改 HBox 中的位置?

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

    您是否只是翻译它们,切换文本,然后重置翻译?

    是的。

    您能否简要介绍一下这是如何工作的?

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

    效果是,当按下增量按钮时,第一个标签中的数字消失,右侧标签中的数字向左移动,一旦到了那里,就会返回到它们的原始位置,但它们的值设置为下一个最高数字。这提供了问题中要求的效果。

    【讨论】:

    • 我不明白这一点,我看到你正在翻译节点,但是,你什么时候改变了 HBox 中的位置?你只是翻译它们,切换文本,然后重置翻译吗?您能否快速概述一下这是如何工作的?那太好了!
    • 感谢您的解释!
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2021-08-17
    • 1970-01-01
    • 1970-01-01
    • 2017-09-18
    • 2014-07-18
    • 2012-04-21
    • 1970-01-01
    相关资源
    最近更新 更多