gpt4 book ai didi

JavaFX - 顺序转换 - 来回播放(一步一步)

转载 作者:行者123 更新时间:2023-12-01 12:42:49 24 4
gpt4 key购买 nike

我正在努力制作一个动画,显示 JavaFX 中二叉搜索树中的搜索。目标是将树节点的值与可能性进行比较的可视化:

随时暂停和播放能够向后播放动画(至少后退一步),使用户能够逐步或一次性播放整个动画。

The preview of visualization

我的愿景是在一个 SequentialTransition(ST) 中添加一系列 TranslateTransitions(TT)。如果动画被标记为“逐步”,则每个 TT 都会在其 OnFinished 处理程序中暂停整个 ST。然而,这仅适用于单向。

我的问题是。 保持反向动画流畅且逐步进行的最佳方法是什么?

我在想:

  • 也许会进行另一个反向转换序列(但如何判断从哪一步继续?)
  • 如何使用速率属性? ST运行时可以改变它吗?

非常感谢您的回答。

最佳答案

一般来说,您可以在动画进行时更改其速率属性。使用 SequentialTransition 的想法很吸引人,但它并不像您想象的那么容易。当顺序转换在两个单独转换之间的边界处暂停时,就会出现问题:您没有任何方法可以判断哪个单独转换被视为当前转换(即下一个转换或上一个转换)。因此,当您尝试反转速率并播放时,顺序转换可能会感到困惑,并立即认为它已处于尝试播放的结尾。

您可以通过使用 Animation.getCurrentTime()Animation.jumpTo(...) 来“插入”顺序过渡,从而对此进行一些破解在开始播放任何步骤之前,请按照正确的方向进行少量操作,但我认为单独管理各个转换可能比使用 SequentialTransition 更容易。

下面是使用此技术移动矩形的简单示例:

import java.util.Arrays;
import java.util.List;
import java.util.stream.Collectors;

import javafx.animation.Animation;
import javafx.animation.Animation.Status;
import javafx.animation.TranslateTransition;
import javafx.application.Application;
import javafx.beans.binding.Bindings;
import javafx.beans.binding.BooleanBinding;
import javafx.beans.property.IntegerProperty;
import javafx.beans.property.SimpleIntegerProperty;
import javafx.beans.value.ObservableValue;
import javafx.geometry.Pos;
import javafx.scene.Scene;
import javafx.scene.control.Button;
import javafx.scene.layout.BorderPane;
import javafx.scene.layout.HBox;
import javafx.scene.layout.Pane;
import javafx.scene.paint.Color;
import javafx.scene.shape.Rectangle;
import javafx.stage.Stage;
import javafx.util.Duration;

public class ReverseSequentialTransitionTest extends Application {

@Override
public void start(Stage primaryStage) {
Pane pane = new Pane();
Rectangle rect = new Rectangle(50, 50, 250, 150);
rect.setFill(Color.color(.5, .5, .1));
pane.getChildren().add(rect);

TranslateTransition ttForward = new TranslateTransition(Duration.seconds(1), rect);
ttForward.setFromX(0);
ttForward.setToX(400);
TranslateTransition ttDown = new TranslateTransition(Duration.seconds(1), rect);
ttDown.setFromY(0);
ttDown.setToY(100);
TranslateTransition ttBackward = new TranslateTransition(Duration.seconds(1), rect);
ttBackward.setFromX(400);
ttBackward.setToX(0);
TranslateTransition ttUp = new TranslateTransition(Duration.seconds(1), rect);
ttUp.setFromY(100);
ttUp.setToY(0);

List<Animation> transitions = Arrays.asList(ttForward, ttDown, ttBackward, ttUp);
IntegerProperty nextTransitionIndex = new SimpleIntegerProperty();

Button playButton = new Button("Play Forward");
playButton.setOnAction(event -> {
int index = nextTransitionIndex.get();
Animation anim = transitions.get(index);
anim.setOnFinished(evt -> nextTransitionIndex.set(index+1));
anim.setRate(1);
anim.play();
});

Button reverseButton = new Button("Play backward");
reverseButton.setOnAction(event -> {
int index = nextTransitionIndex.get()-1;
Animation anim = transitions.get(index);
anim.setOnFinished(evt -> nextTransitionIndex.set(index));
anim.setRate(-1);
anim.play();
});

// This is not really part of the answer to the current question, but the
// next three statements just disable the buttons when appropriate.

// This is a binding which is true if and only if any of the transitions are
// currently running:

BooleanBinding anyPlaying = createAnyPlayingBinding(transitions);

// Disable playButton if we are at the end of the last transition, or if
// any transitions are playing:

playButton.disableProperty().bind(
nextTransitionIndex.greaterThanOrEqualTo(transitions.size())
.or(anyPlaying)
);

// Disable reverseButton if we are at the beginning of the first transition,
// or if any transitions are currently playing:

reverseButton.disableProperty().bind(
nextTransitionIndex.lessThanOrEqualTo(0)
.or(anyPlaying));

HBox controls = new HBox(5);
controls.setAlignment(Pos.CENTER);
controls.getChildren().addAll(playButton, reverseButton);


BorderPane root = new BorderPane();
root.setCenter(pane);
root.setBottom(controls);

primaryStage.setScene(new Scene(root, 800, 400));
primaryStage.show();
}

private BooleanBinding createAnyPlayingBinding(List<Animation> transitions) {
return new BooleanBinding() {
{ // Anonymous constructor
// bind to the status properties of all the transitions
// (i.e. mark this binding as invalid if any of the status properties change)
transitions.stream()
.map(Animation::statusProperty)
.forEach(this::bind);
}
@Override
protected boolean computeValue() {
// return true if any of the transitions statuses are equal to RUNNING:
return transitions.stream()
.anyMatch(anim -> anim.getStatus()==Status.RUNNING);
}
};

}

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

在 JDK 7 中,playButton 的事件处理程序如下所示:

playButton.setOnAction(new EventHandler<ActionEvent>() {
@Override
public void handle(ActionEvent event) {
final int index = nextTransitionIndex.get();
Animation anim = transitions.get(index);
anim.setOnFinished(new EventHandler<ActionEvent>() {
@Override
public void handle(ActionEvent evt) {
nextTransitionIndex.set(index + 1) ;
}
});
anim.setRate(1);
anim.play();
}
});

对于 reverseButton 也类似。您还需要将一些内容声明为 finalcreateAnyPlayingBinding 方法类似于

private BooleanBinding createAnyPlayingBinding(final List<Animation> transitions) {
return new BooleanBinding() {
{
for (Animation transition : transitions) {
this.bind(transition.statusProperty();
}
}
@Override
protected boolean computeValue() {
// return true if any of the transitions statuses are equal to RUNNING:
for (Animation anim : transitions) {
if (anim.getStatus() == Status.RUNNING) {
return true ;
}
}
return false ;
}
};

}

关于JavaFX - 顺序转换 - 来回播放(一步一步),我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/24938245/

24 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com