Warm tip: This article is reproduced from stackoverflow.com, please click
animation java javafx timeline

Slot machine TimeLine animation not working

发布于 2020-04-03 23:20:04

I am trying to develop a slot machine reel application. I have a custom reel pane which adds children vertically. When a spin button is clicked children have to move and when the last child reaches a boundary it has to shift its position above the first child. What I did is shown below.

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 file

<?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>

controller

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

main

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

But the children are not moving when button is clicked.Can someone tell what went wrong?

Questioner
Joseph Jose
Viewed
73
c0der 2020-02-04 02:41

A much simpler tool for the needed animation is PauseTransition.
If you do not want to specify the layout in the fxml you can override layoutChildren() as you did but you'll need to disable automatic layout (layoutChildren()) so the animation can change positions:

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

Consider disabling the button while spinning:

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

To make the code mre here is the slot.fxml using publicly available resources:

<?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>

A test application:

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