老虎机时间线动画不起作用

问题描述 投票:1回答:3

我正在尝试开发老虎机卷轴应用程序。我有一个自定义卷轴窗格,可垂直添加子项。单击旋转按钮时,子项必须移动,而最后一个子项到达边界时,子项必须将其位置移动到第一个子项上方。我所做的如下所示。

public class ReelPane extends Pane {

    Timeline timeline = new Timeline();


    @Override
    protected void layoutChildren() {

        List<Node> managed = getChildren();
        double y = 0;
        for (Node node : managed) {
            node.setLayoutX(0);
            node.setLayoutY(y);
            y += node.getBoundsInLocal().getHeight();
        }
    }

    public void spin() {
        List<Node> managed = getChildren();
        double dy = 4;
        for (Node node : managed) {
            timeline.getKeyFrames().addAll(new KeyFrame(Duration.millis(2000),new KeyValue(node.layoutYProperty(),node.getLayoutY()+dy)));

            if(node.getLayoutY()>=600){
                node.setLayoutY(-50);
            }

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

fxml文件

<?import javafx.scene.control.Button?>
<?import javafx.scene.image.*?>
<?import javafx.scene.layout.Pane?>
<?import sample.ReelPane?>
<Pane stylesheets="@css/slot.css"
      xmlns:fx="http://javafx.com/fxml/1"
      xmlns="http://javafx.com/javafx"
      fx:controller="sample.Controller">
    <ReelPane fx:id="reel" styleClass="container">

        <ImageView fitHeight="100" fitWidth="100">
            <Image url="/sample/resources/apple.png"/>
        </ImageView>

        <ImageView fitHeight="100" fitWidth="100">
            <Image url="/sample/resources/diamond.png"/>
        </ImageView>

        <ImageView fitHeight="100" fitWidth="100">
            <Image url="/sample/resources/glass.png"/>
        </ImageView>

        <ImageView fitHeight="100" fitWidth="100">
            <Image url="/sample/resources/grape.png"/>
        </ImageView>

        <ImageView fitHeight="100" fitWidth="100">
            <Image url="/sample/resources/star.png"/>
        </ImageView>
    </ReelPane>

    <Button fx:id="spin" text="SPIN"/>
</Pane>

控制器

package sample;

import javafx.fxml.FXML;
import javafx.scene.control.Button;

public class Controller {

    @FXML
    ReelPane reel;
    @FXML
    Button spin;

    public void initialize() {
        spin.setOnAction(event -> reel.spin());
    }
}

主要

package sample;

import javafx.application.Application;
import javafx.fxml.FXMLLoader;
import javafx.scene.Parent;
import javafx.scene.Scene;
import javafx.stage.Stage;

public class Main extends Application  {

    @Override
    public void start(Stage primaryStage) throws Exception {
         Parent root = 
       FXMLLoader.load(getClass().getResource("resources/fxml/slot.fxml"));
        primaryStage.setScene(new Scene(root, 400, 900));
        primaryStage.show();

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

但是单击按钮时孩子们没有动。有人可以告诉我出了什么问题吗?

java animation javafx timeline
3个回答
0
投票

如果使用Pane,则需要为每个组件分配布局值,以便slot.fxml看起来像:

<ReelPane fx:id="reel">

    <ImageView fitHeight="100" fitWidth="100" layoutX="0." layoutY="0.0">
        <Image url="/.."/>
    </ImageView>

   <ImageView fitHeight="100" fitWidth="100" layoutX="0." layoutY="100.0">
        <Image url="/.."/>
    </ImageView>

    <ImageView fitHeight="100" fitWidth="100" layoutX="0." layoutY="200.0">
         <Image url="/.."/>
    </ImageView>

    <ImageView fitHeight="100" fitWidth="100" layoutX="0." layoutY="300.0">
          <Image url="/.."/>
    </ImageView>

    <ImageView fitHeight="100" fitWidth="100" layoutX="0." layoutY="400.0">
        <Image url="/.."/>
    </ImageView>

</ReelPane>

<Button fx:id="spin" text="SPIN" layoutX="0." layoutY="550.0" />

要进行动画处理,您需要实际更改动画值,在这种情况下为layoutYProperty

import java.util.List;
import javafx.animation.Interpolator;
import javafx.animation.KeyFrame;
import javafx.animation.KeyValue;
import javafx.animation.Timeline;
import javafx.beans.property.DoubleProperty;
import javafx.scene.Node;
import javafx.scene.layout.Pane;
import javafx.util.Duration;

public class ReelPane extends Pane {

    public void spin() {

        Timeline timeline = new Timeline();
        List<Node> managed = getChildren();

        double dy = 100;

        KeyFrame pauseFrame = new KeyFrame(Duration.millis(2000));

        for (Node node : managed) {

            DoubleProperty interpolatorValue = node.layoutYProperty();
            if(node.getLayoutY()>=400){
                node.setLayoutY(-dy);
            }

            KeyValue startKeyValue = new KeyValue(interpolatorValue, node.getLayoutY());
            KeyFrame startKeyFrame = new KeyFrame(Duration.ZERO, startKeyValue);

            KeyValue endKeyValue = new KeyValue(interpolatorValue, node.getLayoutY()+ dy, Interpolator.EASE_BOTH);
            KeyFrame endKeyframe = new KeyFrame(Duration.millis(200), endKeyValue);

            timeline.getKeyFrames().addAll( pauseFrame, startKeyFrame, endKeyframe);
        }

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

考虑在旋转时禁用按钮:

import javafx.fxml.FXML;
import javafx.scene.control.Button;

public class Controller {

    @FXML
    ReelPane reel;
    @FXML
    Button spin;

    public void initialize() {

        spin.setOnAction(event -> {
            reel.spin();
            spin.setDisable(true);
        });
    }
}


enter image description here


编辑:如果您不想在fxml中指定布局,则可以像以前一样覆盖layoutChildren()但是您需要禁用自动布局(layoutChildren()),以便动画可以改变位置:
import java.util.List;
import javafx.animation.Interpolator;
import javafx.animation.KeyFrame;
import javafx.animation.KeyValue;
import javafx.animation.Timeline;
import javafx.beans.property.DoubleProperty;
import javafx.scene.Node;
import javafx.scene.layout.Pane;
import javafx.util.Duration;

public class ReelPane extends Pane {

    private final Timeline timeline;
    private List<Node> managed;
    private double dy;
    private boolean isAnimating = false;

    public ReelPane() {
        timeline = new Timeline();
    }

    @Override
    protected void layoutChildren() {

        if(isAnimating) return;
        managed = getChildren();
        double y = 0;
        for (Node node : managed) {
            node.setLayoutX(0);
            node.setLayoutY(y);
            dy = Math.max(dy, node.getBoundsInLocal().getHeight()); //dy stores the highest 
            y += dy;
        }
    }

    public void spin() {

        isAnimating = true;
        KeyFrame pauseFrame = new KeyFrame(Duration.millis(2000));

        for (Node node : managed) {

            DoubleProperty interpolatorValue = node.layoutYProperty();
            if(node.getLayoutY()>=400){
                node.setLayoutY(-dy);
            }

            KeyValue startKeyValue = new KeyValue(interpolatorValue, node.getLayoutY());
            KeyFrame startKeyFrame = new KeyFrame(Duration.ZERO, startKeyValue);

            KeyValue endKeyValue = new KeyValue(interpolatorValue, node.getLayoutY()+ dy, Interpolator.EASE_BOTH);
            KeyFrame endKeyframe = new KeyFrame(Duration.millis(200), endKeyValue);

            timeline.getKeyFrames().addAll( pauseFrame, startKeyFrame, endKeyframe);
        }

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

0
投票

KeyFrame的所有Timeline全部在同一时间安排。此外,甚至在开始动画之前就执行if语句。

Imho,将所有元素都放置在VBox中,并简单地反复将translateY从等于元素高度的负值重复更改为0,然后在循环结束时将最后一个元素移到最前面会更容易。您可能希望将clip应用于VBox的父级,以隐藏超出单个元素范围的任何内容。

@Override
public void start(Stage primaryStage) throws Exception {
    VBox pane = new VBox();
    Pane parent = new Pane(pane);
    parent.setPrefSize(50, 50);
    parent.setMinSize(Region.USE_PREF_SIZE, Region.USE_PREF_SIZE);
    parent.setMaxSize(Region.USE_PREF_SIZE, Region.USE_PREF_SIZE);
    Timeline timeline = new Timeline(new KeyFrame(Duration.ZERO, new KeyValue(pane.translateYProperty(), -50d)),
            new KeyFrame(Duration.seconds(2), evt -> {
                pane.getChildren().get(pane.getChildren().size() - 1).toBack();
            }, new KeyValue(pane.translateYProperty(), 0d)));
    timeline.setCycleCount(Animation.INDEFINITE);
    for (int i = 0; i < 6; i++) {
        Rectangle rect = new Rectangle(50, 50, Color.RED.interpolate(Color.BLACK, i / 6d));
        pane.getChildren().add(rect);
    }
    parent.setClip(new Rectangle(50, 50));
    timeline.play();
    Scene scene = new Scene(new StackPane(parent), 200, 200);
    primaryStage.setScene(scene);
    primaryStage.show();
}

0
投票

用于所需动画的简单得多的工具是PauseTransition。如果您不想在fxml中指定布局,则可以像但是那样覆盖layoutChildren(),则需要禁用自动布局(layoutChildren()),以便动画可以更改位置:

import java.util.List;
import javafx.animation.PauseTransition;
import javafx.scene.Node;
import javafx.scene.layout.Pane;
import javafx.util.Duration;

public class ReelPane extends Pane {

    private List<Node> managed;
    private double dy, numberOfCchildren;
    private boolean isAnimating = false;
    private static final double PAUSE = 1;

    @Override
    protected void layoutChildren() {

        if(isAnimating) return;
        managed = getChildren();
        numberOfCchildren = managed.size();
        double y = 0;
        for (Node node : managed) {
            node.setLayoutX(0);
            node.setLayoutY(y);
            dy = Math.max(dy, node.getBoundsInLocal().getHeight()); //dy stores the highest
            y += dy;
        }
    }
    public void spin() {

        isAnimating = true;
        PauseTransition pause = new PauseTransition(Duration.seconds(PAUSE));
        pause.setOnFinished(event ->{
            for (Node node : managed) {

                if(node.getLayoutY()>= (numberOfCchildren -1) * dy){
                    node.setLayoutY(-dy);
                }
                node.setLayoutY(node.getLayoutY() +dy);
            }
            pause.play();
        });

        pause.play();
    }
}

考虑在旋转时禁用按钮:

import javafx.fxml.FXML;
import javafx.scene.control.Button;

public class Controller {

    @FXML
    ReelPane reel;
    @FXML
    Button spin;

    public void initialize() {

        spin.setOnAction(event -> {
            reel.spin();
            spin.setDisable(true);
        });
    }
}

将代码mre设置为此处的slot.fxml,使用的是公共资源:

<?import javafx.scene.control.Button?>
<?import javafx.scene.image.*?>
<?import javafx.scene.layout.Pane?>
<?import fx_tests.ReelPane?>

<Pane 
      xmlns:fx="http://javafx.com/fxml/1"
      xmlns="http://javafx.com/javafx"
      fx:controller="fx_tests.sample.Controller">

    <ReelPane fx:id="reel">

        <ImageView fitHeight="100" fitWidth="100">
            <Image url="https://cdn3.iconfinder.com/data/icons/softwaredemo/PNG/128x128/Box_Green.png"/>
        </ImageView>

       <ImageView fitHeight="100" fitWidth="100" >
            <Image url="https://cdn3.iconfinder.com/data/icons/softwaredemo/PNG/128x128/Box_Red.png"/>
        </ImageView>

        <ImageView fitHeight="100" fitWidth="100">
            <Image url="https://cdn3.iconfinder.com/data/icons/softwaredemo/PNG/128x128/Box_Yellow.png"/>
        </ImageView>

        <ImageView fitHeight="100" fitWidth="100" >
           <Image url="https://cdn3.iconfinder.com/data/icons/softwaredemo/PNG/128x128/Box_Blue.png"/>
        </ImageView>

        <ImageView fitHeight="100" fitWidth="100">
            <Image url="https://cdn3.iconfinder.com/data/icons/softwaredemo/PNG/128x128/Box_Orange.png"/>
        </ImageView>

    </ReelPane>

    <Button fx:id="spin" text="SPIN" layoutX="20." layoutY="550.0" />

</Pane>

测试应用程序:

import javafx.application.Application;
import javafx.fxml.FXMLLoader;
import javafx.scene.Parent;
import javafx.scene.Scene;
import javafx.stage.Stage;

public class Main extends Application  {

    @Override
    public void start(Stage primaryStage) throws Exception {
        Parent root =
        FXMLLoader.load(getClass().getResource("slot.fxml"));
        primaryStage.setScene(new Scene(root, 200, 600));
        primaryStage.show();

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

enter image description here

© www.soinside.com 2019 - 2024. All rights reserved.