动画完成后如何反转?

3
我有一个动画列表,我希望能够通过单击“下一步”按钮来播放它们,并通过单击“上一步”按钮将它们回放。所以我可以播放第一个动画,然后播放第二个动画,再倒放第二个动画并达到与播放第一个动画之后相同的位置。
我的问题是动画完成后无法倒放。我知道可以设置autoReverse,但这样每个动画都会立即倒放。
以下是一个动画的示例:
import javafx.animation.TranslateTransition;
import javafx.application.Application;
import javafx.scene.Scene;
import javafx.scene.control.Button;
import javafx.scene.layout.HBox;
import javafx.scene.layout.Pane;
import javafx.scene.layout.VBox;
import javafx.scene.paint.Color;
import javafx.scene.shape.Circle;
import javafx.stage.Stage;
import javafx.util.Duration;

public class AnimTest extends Application {

    @Override
    public void start(Stage stage) throws Exception {
        Circle c = new Circle(5, Color.RED);
        TranslateTransition move = new TranslateTransition(Duration.seconds(2), c);
        move.setByX(10);
        move.setByY(10);

        Button next = new Button("Next");
        Button previous = new Button("Previous");
        next.setOnAction(e -> {
            move.setRate(1);
            move.play();
        });
        previous.setOnAction(e -> {
            move.setRate(-1);
            move.play();
        });

        Pane p = new Pane(c);
        p.setPrefSize(50, 50);
        HBox buttons = new HBox(next, previous);
        VBox root = new VBox(p, buttons);
        stage.setScene(new Scene(root));
        stage.show();
    }

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

在点击“下一页”后,我希望“上一页”将球移回原始位置(实际上是x减10,y减10),而不是倒放“下一页”的动画。

在实践中,我的动画会对场景图中的不同对象进行动画处理,它们可以是并行/顺序转换。对于列表,我保留一个当前位置索引i,并执行以下操作:

    next.setOnAction(e -> {
        Animation move = list.get(i);
        move.setRate(1);
        move.play();
        i++;
    });
    previous.setOnAction(e -> {
        i--;
        Animation move = list.get(i);
        move.setRate(-1);
        move.play();
    });

为了尝试反转之前的动画。

我该怎么做呢?

澄清一下,我的列表是 Animation 类型。 TranslateTransition 只是一个例子。

4个回答

3
这里的问题是使用“相对”移动而不是绝对移动。
如果您设置byX = 10,则动画向前播放时会将节点向右移动10个单位,这意味着正确地反转动画的方法是立即将节点放置在最终位置,然后将节点移回原始位置,然后开始动画。
由于您不想一遍又一遍地使用同一动画,因此对于使用“相对”值的动画,找到正确的反转方式可能会很困难。如果改用绝对值,则简单地播放动画的反向版本不应该引起任何问题。
示例:
@Override
public void start(Stage stage) {
    Circle c = new Circle(5, Color.RED);

    // create alternating right/down movement animations with absolute movement
    List<Animation> animations = new ArrayList<>(10);
    for (int i = 0; i < 10; i++) {
        TranslateTransition move = new TranslateTransition(Duration.seconds(1), c);
        animations.add(move);
        int step = i >> 1;
        if ((i & 1) == 0) {
            move.setFromX(step * 10);
            move.setToX((step + 1) * 10);
        } else {
            move.setFromY(step * 10);
            move.setToY((step + 1) * 10);
        }
    }

    final ListIterator<Animation> iterator = animations.listIterator();
    
    Button next = new Button("Next");
    Button previous = new Button("Previous");
    previous.setDisable(true);
    
    next.setOnAction(e -> {
        Animation move = iterator.next();
        
        next.setDisable(!iterator.hasNext());
        previous.setDisable(false);
        
        move.setRate(1);
        move.play();
    });
    
    previous.setOnAction(e -> {
        Animation move = iterator.previous();
        
        next.setDisable(false);
        previous.setDisable(!iterator.hasPrevious());
        
        move.setRate(-1);
        move.play();
    });

    Pane p = new Pane(c);
    p.setPrefSize(100, 100);
    HBox buttons = new HBox(next, previous);
    VBox root = new VBox(p, buttons);
    stage.setScene(new Scene(root));
    stage.show();
}

那么对于一般的动画(就像我说的,我的通常是顺序和并行转换),这是不可能做到的吗?自动反转的第二个周期可以做到。我想知道是否有可能“捕获和存储”第二个(反向)周期,并稍后播放它。如果动画内置了这个功能,我也应该能够做到吧? - Mark
为什么不可能呢,只要SequentialTransitionParallelTransition的所有部分都是“绝对”的而不是“相对”的,就应该可以做到。autoReverse和你的代码之间的区别在于,Transition知道如何正确地反转自己,但是如果你想要完全控制动画播放的方向(RotateTransition需要不同的反转逻辑,而TranslateTransition则不同),您需要自行实现此逻辑。 - fabian
我的意思是,这仅适用于我自己知道如何数学上反转的动画,是绝对的,并且我需要提前准备好它。如果给定一个“Animation”列表,是否能够完成? - Mark
如果你感兴趣的话,我用一个技巧解决了这个问题。如果你想看一下,我会很感激的。 - Mark

0

我成功地通过使用 PauseTransition 来“存储”反向循环以供以后使用。该动画在正向循环后暂停,然后可以从第二个循环播放并反转。这不是最好的解决方案,但它有效(除非您按下按钮太快)。我尝试使用注释代码来解决它,但它没有完全解决,如果有人有解决方案,请告诉我。

import java.util.ArrayList;
import java.util.List;

import javafx.animation.Animation;
import javafx.animation.ParallelTransition;
import javafx.animation.PauseTransition;
import javafx.animation.TranslateTransition;
import javafx.application.Application;
import javafx.scene.Scene;
import javafx.scene.control.Button;
import javafx.scene.control.Label;
import javafx.scene.layout.HBox;
import javafx.scene.layout.Pane;
import javafx.scene.layout.VBox;
import javafx.scene.paint.Color;
import javafx.scene.shape.Circle;
import javafx.stage.Stage;
import javafx.util.Duration;

public class AnimTest extends Application {

    int current = 0;
    final int size = 5;

    @Override
    public void start(Stage stage) throws Exception {
        Circle c = new Circle(10, Color.RED);

        List<Animation> animations = new ArrayList<>(size);
        for (int i = 0; i < size; i++) {
            TranslateTransition move = new TranslateTransition(Duration.seconds(1), c);
            move.setByX(20);
            move.setByY(20);
            PauseTransition pauser = new PauseTransition(move.getCycleDuration());
            ParallelTransition parallel = new ParallelTransition(move, pauser);
            pauser.setOnFinished(e -> parallel.pause());
            parallel.setCycleCount(2);
            parallel.setAutoReverse(true);
            animations.add(parallel);
        }

        Button next = new Button("Next");
        Button previous = new Button("Previous");
        previous.setDisable(true);

        Label l = new Label(current + "");

        next.setOnAction(e -> {
            next.setDisable(current == size - 1);
            previous.setDisable(false);

/*          if (current > 0) {
                Animation last = animations.get(current - 1);
                last.jumpTo(last.getCycleDuration());
            }*/
            Animation cur = animations.get(current);
            cur.playFromStart();

            current++;
            l.setText(current + "");
        });

        previous.setOnAction(e -> {
            current--;
            l.setText(current + "");

            next.setDisable(false);
            previous.setDisable(current == 0);

/*          if (current < size - 1) {
                Animation last = animations.get(current + 1);
                last.stop();
            }*/
            Animation cur = animations.get(current);
            cur.play();
        });

        Pane p = new Pane(c);
        p.setPrefSize(200, 200);
        HBox buttons = new HBox(5, next, previous, l);
        VBox root = new VBox(p, buttons);
        stage.setScene(new Scene(root));
        stage.show();
    }

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

我使用了来自Fabian的禁用/启用按钮代码(+1)。


0
我发现最快的解决方案是添加一个监听器到currentRateProperty,监听它是否变为-1并在第一个周期完成后暂停转换。 没有cycleCompleted监听器或任何类似的监听器,但可以通过这种方式实现监听周期完成。 请注意,cycleCount必须设置为2,autoReverse必须设置为true。
    Node node = ...;
    double byX = ...;
    
    TranslateTransition transition = new TranslateTransition();
    transition.setNode(node);
    transition.setCycleCount(2);
    transition.setAutoReverse(true);
    transition.setByX(byX);

    transition.currentRateProperty().addListener((obs, old, now) -> {
        if (now.intValue() == -1) {
            transition.pause();
        }
    });
    
    Button play = new Button("Play");
    play.setOnAction(event -> transition.play());

请注意,正向和反向转换都由同一个方法调用 transition.play() 触发,因此只有一个按钮用于两种动作,但这当然可以更改。但就我个人而言,我喜欢这样。
在您的情况下,它会像这样:
public class AnimTest extends Application {

    @Override
    public void start(Stage stage) throws Exception {
        Circle c = new Circle(5, Color.RED);
        TranslateTransition move = new TranslateTransition(Duration.seconds(2), c);
        move.setAutoReverse(true);
        move.setCycleCount(2);
        move.setByX(10);
        move.setByY(10);

        move.currentRateProperty().addListener((obs, old, now) -> {
            if (now.intValue() == -1) {
                move.pause();
            }
        });

        Button next = new Button("Next/Previous");
        next.setOnAction(e -> {
            move.play();
        });

        Pane p = new Pane(c);
        p.setPrefSize(50, 50);
        HBox buttons = new HBox(next);
        VBox root = new VBox(p, buttons);
        stage.setScene(new Scene(root));
        stage.show();
    }

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

-1
要在Java中反转动画,首先必须将动画的autoReverse属性设置为true,并将cycleCount设置为2。下面是我之前编写的一个简单的代码片段,使用了上述内容。
    ScaleTransition scaleTransition = new ScaleTransition(duration, btn);
    scaleTransition.setByX(1.2);
    scaleTransition.setByY(1.2);
    scaleTransition.setAutoReverse(true);
    scaleTransition.setCycleCount(2);
    scaleTransition.play();

这不会按照Mark的要求工作,因为它会立即播放反向动画。问题在于,反向动画应该由一个按钮触发。 - Trizion

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接