【问题标题】:Label doesn't resize width smoothly using an animation标签没有使用动画平滑地调整宽度
【发布时间】:2021-03-08 20:04:25
【问题描述】:

我编写了一个 JavaFX 应用程序。
我想每 10ms 更新一次标签宽度,其中标签代表一个值。
一般来说,我成功地做到了这一点,但有一个问题:标签 闪烁 带有 small values,与 big values 相反。
在我之前的作品中,我注意到 Progressbar 控件具有平滑的大小调整动画。
所以,我想让它像 Progressbar 一样工作(顺利)。


这是解释问题的示例代码:

import javafx.animation.KeyFrame;
import javafx.animation.Timeline;
import javafx.application.Application;
import javafx.event.ActionEvent;
import javafx.event.EventHandler;
import javafx.geometry.Insets;
import javafx.scene.Scene;
import javafx.scene.control.Label;
import javafx.scene.layout.VBox;
import javafx.stage.Stage;
import javafx.util.Duration;


public class MainApplication extends Application {

    private static final String LABEL_STYLE = "-fx-font-weight: bold; -fx-border-style: solid; -fx-pref-height: 38; -fx-background-color: ";
    private static final double MAX_WIDTH = 760;
    private static final double MAX_VALUE = 60000;
    private static final double VALUE_PER_PIXEL_RATIO = MAX_VALUE / MAX_WIDTH;
    private static final int ANIMATION_DURATION = 9000;
    private static final int KEYFRAME_DURATION = 10;
    private static final int ANIMATION_CYCLES = ANIMATION_DURATION / KEYFRAME_DURATION;

    private final Label label_1 = new Label();
    private final Label label_2 = new Label();
    private final Label label_3 = new Label();



    @Override
    public void start(Stage primaryStage) {

        label_1.setStyle(LABEL_STYLE + " #4363d8; ");
        label_2.setStyle(LABEL_STYLE + " #ffe119; ");
        label_3.setStyle(LABEL_STYLE + " #3cb44b; ");

        VBox root = new VBox();
        root.setSpacing(10);
        root.setPadding(new Insets(40, 30, 30, 30));
        root.getChildren().addAll(label_1, label_2, label_3);
        root.getChildren().addAll(new Label("Label 1 : Good (Smooth)"),
                new Label("Label 2 : Acceptable"),
                new Label("Label 3 : Weak (Blinks)"));


        Scene scene = new Scene(root);

        primaryStage.setScene(scene);
        primaryStage.setWidth(800);
        primaryStage.setHeight(350);
        primaryStage.show();

        playAnimation();

    }


    private void playAnimation() {

        MyData myData_1 = new MyData(58000, ANIMATION_CYCLES, VALUE_PER_PIXEL_RATIO);
        MyData myData_2 = new MyData(20000, ANIMATION_CYCLES, VALUE_PER_PIXEL_RATIO);
        MyData myData_3 = new MyData(4000, ANIMATION_CYCLES, VALUE_PER_PIXEL_RATIO);

        final Timeline timeline = new Timeline();
        timeline.setCycleCount(ANIMATION_CYCLES);

        timeline.getKeyFrames().add(new KeyFrame(Duration.millis(KEYFRAME_DURATION), new EventHandler<ActionEvent>() {
            @Override public void handle(ActionEvent actionEvent) {

                label_1.setPrefWidth(myData_1.getCurrentWidth());
                label_2.setPrefWidth(myData_2.getCurrentWidth());
                label_3.setPrefWidth(myData_3.getCurrentWidth());
            }

        }));

        timeline.play();

        // Replay animation
        timeline.setOnFinished(event -> playAnimation());

    }

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

class MyData  {

    private double currentValue;
    private double currentWidth;
    private double incrementStep;
    private static double VALUE_PER_PIXEL_RATIO;

    public MyData(int maxValue, double cycles, double ratio) {
        super();
        this.incrementStep = maxValue / cycles;
        VALUE_PER_PIXEL_RATIO = ratio;
    }

    public double getCurrentWidth() {
        currentWidth = currentValue / VALUE_PER_PIXEL_RATIO;
        currentValue += incrementStep;
        return currentWidth;
    }
}

谢谢。

【问题讨论】:

  • 请使用java命名约定

标签: java animation javafx progress-bar timeline.js


【解决方案1】:

它不会闪烁,在 Debian Linux 上使用 JavaFX 15,并带有 Intel 集成图形芯片。

您自己做了很多工作来为标签设置动画。与其自己做动画,不如让 Timeline 做插值:

private void animate(Label label,
                     double value) {

    Timeline timeline = new Timeline(
        1000 / 10,  // every 10 ms
        new KeyFrame(Duration.millis(58000 / value * animationDuration),
            new KeyValue(label.prefWidthProperty(), 800 - 60)));

    timeline.setCycleCount(Timeline.INDEFINITE);
    timeline.play();
}

private void playAnimation() {
    animate(label_1, 58000);
    animate(label_2, 20000);
    animate(label_3, 4000);
}

我不确定强制每秒 100 帧是否真的有用。 JavaFX 似乎很擅长在不指定帧速率的情况下创建流畅的动画:

private void animate(Label label,
                     double value) {

    Timeline timeline = new Timeline(
        new KeyFrame(Duration.millis(58000 / value * animationDuration),
            new KeyValue(label.prefWidthProperty(), 800 - 60)));

    timeline.setCycleCount(Timeline.INDEFINITE);
    timeline.play();
}

private void playAnimation() {
    animate(label_1, 58000);
    animate(label_2, 20000);
    animate(label_3, 4000);
}

【讨论】:

  • 感谢您的回答。我所说的闪烁是指可见的小跳跃(绿色标签长大,带有小可见跳跃(闪烁),不像蓝色标签)。你的代码给了我和我的代码一样的结果,即使我有和你一样的环境(Intel i5、8GB RAM、1GB GPU、JavaFx 15、Linux Mint)。
  • 我忘了说我使用 EventHandler() 而不是 KeyValue,因为我在 Timeline 中还有其他任务要做。
  • 我看到了你所说的“眨眼”效果;第三个小节的动画不流畅。我不知道是什么原因造成的。
  • 我注意到只有很小的跳跃。谢谢。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2014-12-17
  • 1970-01-01
  • 2013-04-30
  • 1970-01-01
  • 2017-06-24
  • 1970-01-01
相关资源
最近更新 更多