简体   繁体   English

完成后如何反转动画?

[英]How to reverse animation after it has finished?

I have a list of animations and I want to be able to play them by clicking on a "next" button and playing them back by clicking a "previous" button.我有一个动画列表,我希望能够通过单击“下一个”按钮来播放它们,并通过单击“上一个”按钮来播放它们。 So I can play the first animation, then play the 2nd animation, then play the 2nd animation backwards and reach the position like after playing the first animation only.所以我可以播放第一个动画,然后播放第二个动画,然后向后播放第二个动画,并到达仅播放第一个动画后的位置。

My problem is that I can't reverse the animation after it's finished.我的问题是动画完成后我无法反转动画。 I know that I can set autoReverse but then each animation will reverse immediately.我知道我可以设置autoReverse但每个动画都会立即反转。

Here is an example for one animation:这是一个动画的示例:

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

After pressing "next" I want "previous" to move the ball back to its original position (so effectively x by -10 and y by -10) and not playing the "following" animation in reverse.按下“下一个”后,我希望“上一个”将球移回其原始位置(有效地 x x -10 和 y x -10),而不是反向播放“跟随”动画。

In practice, my animations animate different objects in the scenegraph and they can be parallel/sequential transitions.在实践中,我的动画为场景图中的不同对象设置动画,它们可以是并行/顺序转换。 For the list I keep a current location index i and doing:对于列表,我保留当前位置索引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();
    });

in an attempt to reverse the previous animation.试图反转之前的动画。

How can I do this?我怎样才能做到这一点?

To clarify, my list is of Animation .澄清一下,我的清单是Animation The TranslateTransition was just an example. TranslateTransition只是一个例子。

The issue here is using "relative" movement instead of absolute movement.这里的问题是使用“相对”运动而不是绝对运动。

If you set byX = 10 the animation moves the node 10 to the right when played forward which means the proper way of reversing the animation would be to place the node at the end position immediately and then moving the node back to the original location before starting the animation.如果设置byX = 10 ,则动画在向前播放时将节点 10 向右移动,这意味着反转动画的正确方法是立即将节点放置在结束位置,然后在开始之前将节点移回原始位置动画。

Since you don't want to use the same animation over and over again finding the correct way to invert different animations could be difficult for animations using "relative" values.由于您不想一遍又一遍地使用相同的动画,因此对于使用“相对”值的动画来说,找到反转不同动画的正确方法可能很困难。 If you instead use absolute ones this shouldn't simply playing the animations backwards shouldn't cause any issues.如果您改为使用绝对动画,则不应简单地向后播放动画,这不会引起任何问题。

Example例子

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

I managed to trick my way into "storing" the reverse cycle for later use using a PauseTransition that pauses the animation after the forward cycle.我设法使用在正向循环后暂停动画的PauseTransition来“存储”反向循环以供以后使用。 Then the animation can be played from the second cycle and it will reverse.然后动画可以从第二个循环开始播放,它会反转。 Not the pretiest solution but it works (except for when you press the buttons too quickly. I tried to solve it with the comment code but it didn't quite get there so if anyone has a solution please tell)不是最漂亮的解决方案,但它可以工作(除非你按下按钮太快。我试图用评论代码解决它,但它并没有完全到达那里,所以如果有人有解决方案,请告诉)

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

I used the disable/enable buttons code from fabian (+1).我使用了 fabian (+1) 的禁用/启用按钮代码。

I found the quickest solution is to add a listener to the currentRateProperty, listen for it to change to -1 and pause the transition (after the first cycle completes).我发现最快的解决方案是向 currentRateProperty 添加一个监听器,监听它以更改为 -1 并暂停转换(在第一个周期完成后)。 There is no cycleCompleted listener or any similar listener, but listening for the cycle to complete can be achieved that way.没有 cycleCompleted 侦听器或任何类似的侦听器,但是可以通过这种方式实现侦听循环完成。 Note that the cycleCount must be set to 2 , and autoReverse must be set to true .请注意, 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());

Note that both the forward and backward translation are triggered by the same method call transition.play() , hence there is only one button for both motions, but this of course can be changed.请注意,向前和向后平移都是由同一个方法调用transition.play()触发的,因此两个动作只有一个按钮,但这当然可以更改。 But personally, i like it that way.但就个人而言,我喜欢这样。

In your case, it would look like this:在您的情况下,它看起来像这样:

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

To reverse an animation in java, first you have to set the animation's autoReverse property to true and also set the cycleCount to 2. Below is a simple code snippet i wrote earlier that makes use of the things stated above.要在 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();

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM