需要帮助动画图像遵循 JavaFX 中的圆形路径

Need help animating an image follow a circular path in JavaFX

我正在做一个大学项目,我必须重新制作音乐椅游戏,我正在为孩子们制作围着椅子转圈的动画。我一直在尝试用沿着圆形路径的矩形进行测试。 问题是我有一个带工具栏的 HBox,小游戏稍后会用到它,而在堆栈窗格中有带路径的矩形,这两件事在 VBox 中,基本上是根。但是矩形在 window 的右下角做动画,我想把它放在堆栈窗格的中心。任何帮助将不胜感激。这是代码:

/*
 * To change this license header, choose License Headers in Project Properties.
 * To change this template file, choose Tools | Templates
 * and open the template in the editor.
 */
package javafxapplication4;

import java.io.FileInputStream;
import java.io.FileNotFoundException;
import javafx.animation.PathTransition;
import javafx.animation.PathTransition.OrientationType;
import javafx.animation.Timeline;
import javafx.application.Application;
import javafx.event.ActionEvent;
import javafx.event.EventHandler;
import javafx.geometry.Insets;
import javafx.geometry.Pos;
import javafx.scene.Scene;
import javafx.scene.control.Button;
import javafx.scene.control.Label;
import javafx.scene.image.Image;
import javafx.scene.image.ImageView;
import javafx.scene.layout.Background;
import javafx.scene.layout.BackgroundFill;
import javafx.scene.layout.CornerRadii;
import javafx.scene.layout.GridPane;
import javafx.scene.layout.HBox;
import javafx.scene.layout.Pane;
import javafx.scene.layout.Priority;
import javafx.scene.layout.StackPane;
import javafx.scene.layout.VBox;
import javafx.scene.paint.Color;
import javafx.scene.shape.ArcTo;
import javafx.scene.shape.ClosePath;
import javafx.scene.shape.MoveTo;
import javafx.scene.shape.Path;
import javafx.scene.shape.Rectangle;
import javafx.stage.Stage;
import javafx.util.Duration;
import logica.CircularPane;

/**
 *
 * @author Franklin
 */
public class JavaFXApplication4 extends Application {
    
    @Override
    public void start(Stage primaryStage) throws FileNotFoundException {
        VBox boxmaster=new VBox();
        
        StackPane stackpane=new StackPane();
        
        HBox tools=new HBox(); tools.setPrefHeight(70);
        
        
        //Botones y tool bar
        
        //start
        
        Button start=new Button();
        start.setPadding(new Insets(-1,-1,-1,-1));
        start.setAlignment(Pos.CENTER_LEFT);
        tools.getChildren().add(start);
        
        //Cambio de dirección
        HBox botones=new HBox(); botones.setSpacing(5);
        botones.setAlignment(Pos.CENTER_RIGHT);
        //izquierda
        
        Button left=new Button();
        left.setPadding(new Insets(-1,-1,-1,-1));
        left.setStyle("-fx-background-radius: 5em; " +
                "-fx-min-width: 50px; " +
                "-fx-min-height: 50px; " +
                "-fx-max-width: 50px; " +
                "-fx-max-height: 50px;");
        botones.getChildren().add(left);
        
        //derecha
        
        Button right=new Button();
        right.setPadding(new Insets(-1,-1,-1,-1));
        right.setStyle("-fx-background-radius: 5em; " +
                "-fx-min-width: 50px; " +
                "-fx-min-height: 50px; " +
                "-fx-max-width: 50px; " +
                "-fx-max-height: 50px;");
        botones.getChildren().add(right);
        
        //Pause
        
        Button pause=new Button();
        pause.setPadding(new Insets(-1,-1,-1,-1));
        pause.setStyle("-fx-background-radius: 5em; " +
                "-fx-min-width: 50px; " +
                "-fx-min-height: 50px; " +
                "-fx-max-width: 50px; " +
                "-fx-max-height: 50px;");
        botones.getChildren().add(pause);
        
        //Salir
        Button exit=new Button(); 
        exit.setStyle("-fx-background-radius: 5em; " +
                "-fx-min-width: 55px; " +
                "-fx-min-height: 55px; " +
                "-fx-max-width: 55px; " +
                "-fx-max-height: 55px;");
        exit.setVisible(false);
        botones.getChildren().add(exit);
        
        HBox.setHgrow(botones, Priority.ALWAYS);
        HBox.setMargin(start, new Insets(14,0,0,40));
        
        tools.getChildren().add(botones);
        tools.setPadding(new Insets(2));
        
        boxmaster.getChildren().add(tools);
        
        //Juego como tal
        Rectangle rect2 = new Rectangle(20, 20);
        rect2.setArcHeight(10);
        rect2.setArcWidth(10);
        rect2.setFill(Color.GREEN);
        stackpane.getChildren().add(rect2);
        
        Path path2 = createEllipsePath(0, 0, 150, 150, 0);
        stackpane.getChildren().add(path2);
        
        PathTransition pathCircle=new PathTransition();
        pathCircle.setDuration(Duration.seconds(2));
        pathCircle.setPath(path2);
        pathCircle.setNode(rect2);
        pathCircle.setOrientation(OrientationType.ORTHOGONAL_TO_TANGENT);
        pathCircle.setCycleCount(Timeline.INDEFINITE);
        pathCircle.setAutoReverse(false);
        
        stackpane.setAlignment(Pos.CENTER);
        boxmaster.getChildren().add(stackpane);
        VBox.setVgrow(stackpane, Priority.ALWAYS);
        Scene scene=new Scene(boxmaster,1080,720);
        primaryStage.setScene(scene);
        primaryStage.setResizable(false);
        primaryStage.show();
        pathCircle.play();
    }
    
    private Path createEllipsePath(double centerX, double centerY, double radiusX, double radiusY, double rotate)
    {
        ArcTo arcTo = new ArcTo();
        arcTo.setX(centerX - radiusX + 1); // to simulate a full 360 degree celcius circle.
        arcTo.setY(centerY - radiusY);
        arcTo.setSweepFlag(false);
        arcTo.setLargeArcFlag(true);
        arcTo.setRadiusX(radiusX);
        arcTo.setRadiusY(radiusY);
        arcTo.setXAxisRotation(rotate);

        Path path = new Path();
        path.getElements().addAll(
                new MoveTo(centerX - radiusX, centerY - radiusY),
                arcTo,
                new ClosePath()); // close 1 px gap.
        path.setStroke(Color.DODGERBLUE);
        path.getStrokeDashArray().setAll(5d, 5d);
        return path;
    }

    /**
     * @param args the command line arguments
     */
    public static void main(String[] args) {
        launch(args);
    }
    
}

NOTES= createEllipsePath 代码是我在研究如何创建圆形路径后发现的。 按钮没有文字,因为它们最初有图形。

我不是 JavaFX 方面的专家。我只是玩弄了你的代码,我让它按照我的想法(并希望)你想要它做。我添加了额外的 Path。第一个 Path 绘制圆,第二个 Path 分配给 PathTransition。然后我玩弄第二个 Path 的坐标,直到我得到绿色矩形以围绕第一个路径的圆圈移动。

请注意,我从您的代码中删除了未使用的导入,并为您的每个 Button 添加了不同的字母,这样我就可以看到它们在 Scene.

中的位置

这是代码。

public class JavaFXApplication4 extends Application {

    @Override
    public void start(Stage primaryStage) { //throws FileNotFoundException {
        VBox boxmaster = new VBox();
        StackPane stackpane = new StackPane();
        HBox tools = new HBox();
        tools.setPrefHeight(70);

        //Botones y tool bar

        //start

        Button start = new Button("S");
        start.setPadding(new Insets(-1,-1,-1,-1));
        start.setAlignment(Pos.CENTER_LEFT);
        tools.getChildren().add(start);

        //Cambio de dirección
        HBox botones = new HBox();
        botones.setSpacing(5);
        botones.setAlignment(Pos.CENTER_RIGHT);
        //izquierda
        
        Button left = new Button("L");
        left.setPadding(new Insets(-1,-1,-1,-1));
        left.setStyle("-fx-background-radius: 5em; " +
                      "-fx-min-width: 50px; " +
                      "-fx-min-height: 50px; " +
                      "-fx-max-width: 50px; " +
                      "-fx-max-height: 50px;");
        botones.getChildren().add(left);
        
        //derecha
        
        Button right = new Button("R");
        right.setPadding(new Insets(-1,-1,-1,-1));
        right.setStyle("-fx-background-radius: 5em; " +
                       "-fx-min-width: 50px; " +
                       "-fx-min-height: 50px; " +
                       "-fx-max-width: 50px; " +
                       "-fx-max-height: 50px;");
        botones.getChildren().add(right);
        
        //Pause
        
        Button pause = new Button("P");
        pause.setPadding(new Insets(-1,-1,-1,-1));
        pause.setStyle("-fx-background-radius: 5em; " +
                       "-fx-min-width: 50px; " +
                       "-fx-min-height: 50px; " +
                       "-fx-max-width: 50px; " +
                       "-fx-max-height: 50px;");
        botones.getChildren().add(pause);
        
        //Salir
        Button exit = new Button("E");
        exit.setStyle("-fx-background-radius: 5em; " +
                      "-fx-min-width: 55px; " +
                      "-fx-min-height: 55px; " +
                      "-fx-max-width: 55px; " +
                      "-fx-max-height: 55px;");
        exit.setVisible(false);
        botones.getChildren().add(exit);
        
        HBox.setHgrow(botones, Priority.ALWAYS);
        HBox.setMargin(start, new Insets(14, 0, 0, 40));
        
        tools.getChildren().add(botones);
        tools.setPadding(new Insets(2));
        
        boxmaster.getChildren().add(tools);
        
        //Juego como tal
        Rectangle rect2 = new Rectangle(20, 20);
        rect2.setArcHeight(10);
        rect2.setArcWidth(10);
        rect2.setFill(Color.GREEN);
        stackpane.getChildren().add(rect2);
        
        Path path2 = createEllipsePath(166, 10, 160, 160, 0);  // changed this line.
        Path path1 = createEllipsePath(0, 0, 150, 150, 0);  // added this line.
        stackpane.getChildren().add(path1);
        
        PathTransition pathCircle = new PathTransition();
        pathCircle.setDuration(Duration.seconds(2));
        pathCircle.setPath(path2);
        pathCircle.setNode(rect2);
        pathCircle.setOrientation(OrientationType.ORTHOGONAL_TO_TANGENT);
        pathCircle.setCycleCount(Timeline.INDEFINITE);
        pathCircle.setAutoReverse(false);
        
        stackpane.setAlignment(Pos.CENTER);
        boxmaster.getChildren().add(stackpane);
        VBox.setVgrow(stackpane, Priority.ALWAYS);
        Scene scene=new Scene(boxmaster,1080,720);
        primaryStage.setScene(scene);
        primaryStage.setResizable(false);
        primaryStage.show();
        pathCircle.play();
    }
    
    private Path createEllipsePath(double centerX, double centerY, double radiusX, double radiusY, double rotate)
    {
        ArcTo arcTo = new ArcTo();
        arcTo.setX(centerX - radiusX + 1); // to simulate a full 360 degree celcius circle.
        arcTo.setY(centerY - radiusY);
        arcTo.setSweepFlag(false);
        arcTo.setLargeArcFlag(true);
        arcTo.setRadiusX(radiusX);
        arcTo.setRadiusY(radiusY);
        arcTo.setXAxisRotation(rotate);

        Path path = new Path();
        path.getElements().addAll(
                new MoveTo(centerX - radiusX, centerY - radiusY),
                arcTo,
                new ClosePath()); // close 1 px gap.
        path.setStroke(Color.DODGERBLUE);
        path.getStrokeDashArray().setAll(5d, 5d);
        return path;
    }

    /**
     * @param args the command line arguments
     */
    public static void main(String[] args) {
        launch(args);
    }
}