英文:
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 Button
s 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);
}
}
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。
评论