需要帮助在JavaFX中实现图像沿着圆形路径进行动画。

huangapple go评论59阅读模式
英文:

Need help animating an image follow a circular path in JavaFX

问题

我正在进行一个大学项目,需要重新创建音乐椅游戏。我目前正在实现孩子们围着椅子转圈的动画部分。我尝试制作一个沿着圆形路径移动的矩形测试。问题是,我有一个包含工具栏的HBox,迷你游戏以后会用到这个工具栏,而矩形和路径则在StackPane中,这两者都在一个VBox中,基本上是根容器。但矩形的动画在窗口的右下角进行,我想把它放在StackPane的中心。任何帮助将不胜感激。以下是代码部分:

@Override
public void start(Stage primaryStage) throws FileNotFoundException {
    VBox boxmaster = new VBox();
    
    StackPane stackpane = new StackPane();
    
    HBox tools = new HBox();
    
    // ... (省略了按钮和工具栏的设置部分)
    
    boxmaster.getChildren().add(tools);
    
    // 游戏主体
    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);
    
    // ... (省略了动画设置部分)
    
    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);
    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());
    path.setStroke(Color.DODGERBLUE);
    path.getStrokeDashArray().setAll(5d, 5d);
    return path;
}

请注意,createEllipsePath 方法用于创建圆形路径,按钮没有文本,因为原本它们具有图像。

英文:

I'm working on a university project where I have to recreate the game of musical chairs, I'm at the part of animating the kids going in a circle around chairs. I have been trying to make a test with a rectangle following a circular path.
The problem is that I have an HBox with a tools bar that the mini game will need for later and the rectangle with the path in an stackpane, this two things are in a VBox that basically is the root. But the rectangle does the animation in the bottom right corner of the window and I would like to make it at the center of the stackpane. Any help would be apreciated. Here is the code:

/*
* 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= The createEllipsePath code is what I found after digging on how to create a circular path.
The buttons do not have text, because originally they have a graphic.

答案1

得分: 1

我不是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);

        //按钮和工具栏

        //开始按钮

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

        //方向更改按钮
        HBox botones = new HBox();
        botones.setSpacing(5);
        botones.setAlignment(Pos.CENTER_RIGHT);

        //左按钮
        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);

        //右按钮
        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);

        //暂停按钮
        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);

        //退出按钮
        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);

        //游戏部分
        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);  // 更改了这一行。
        Path path1 = createEllipsePath(0, 0, 150, 150, 0);  // 添加了这一行。
        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); // 模拟完整的360度圆圈。
        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()); // 关闭1像素的间隙。
        path.setStroke(Color.DODGERBLUE);
        path.getStrokeDashArray().setAll(5d, 5d);
        return path;
    }

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

I'm not an expert in JavaFX. I just played around with your code and I got it to do what I think (and hope) you want it to do. I added an extra Path. The first Path draws the circle and the second Path is assigned to the PathTransition. I then played around with the coordinates of the second Path until I got the green rectangle to move around the circle that is the first path.

Note that I removed unused imports from your code and added a different letter to each of your Buttons just so I could see where they appear in the Scene.

Here is the code.

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

huangapple
  • 本文由 发表于 2020年7月25日 14:25:53
  • 转载请务必保留本文链接:https://go.coder-hub.com/63085114.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定