I am trying to make an animation in which the car moves from left to right. If it reaches the right end, it starts over again. This type of animation is easy with PathTransition. But I have to change the car speed through the keys UP/DOWN during animation. For some reason I am not able to do that with PathTransition.
So, I am making a simple animation. But the car is not moving in this case. Can someone help me find my mistake:
package exercise_15_29;
import javafx.animation.Timeline;
import javafx.application.Application;
import javafx.scene.Group;
import javafx.scene.Scene;
import javafx.scene.input.KeyCode;
import javafx.scene.input.KeyEvent;
import javafx.scene.layout.Pane;
import javafx.scene.paint.Color;
import javafx.scene.shape.Circle;
import javafx.scene.shape.Line;
import javafx.scene.shape.Polygon;
import javafx.scene.shape.Rectangle;
import javafx.stage.Stage;
import javafx.util.Duration;
import javafx.animation.KeyFrame;
public class Exercise_15_29 extends Application {
static Group car = new Group();
Circle wheel1 = new Circle(15, 95, 5);
Circle wheel2 = new Circle(35, 95, 5);
Polygon body1 = new Polygon();
Rectangle body2 = new Rectangle(0.0, 80.0, 50, 10);
Line path = new Line(0, 90, 500, 90);
int speed = 100;
boolean play = true;
public static void main(String[] args) {
launch(args);
}
public static void moveCar(){
if(car.getLayoutX() == 500)
car.setTranslateX(-500);
else
car.setTranslateX(10);
}
#Override
public void start(Stage primaryStage) {
body1.getPoints().addAll(new Double[]{
10.0, 80.0,
20.0, 70.0,
30.0, 70.0,
40.0, 80.0
});
body1.setFill(Color.BLUE);
body2.setFill(Color.SKYBLUE);
path.setVisible(false);
car.getChildren().addAll(wheel1, wheel2, body1, body2);
Timeline animation = new Timeline(new KeyFrame(Duration.millis(speed),e -> moveCar()));
animation.setCycleCount(Timeline.INDEFINITE);
animation.play();
Pane root = new Pane();
root.getChildren().add(car);
root.getChildren().add(path);
Scene scene = new Scene(root, 500, 100);
scene.addEventFilter(KeyEvent.KEY_PRESSED, e -> {
if (e.getCode() == KeyCode.P) {
if (play)
animation.pause();
else
animation.play();
play = !play;
}
});
primaryStage.setTitle("Hello World!");
primaryStage.setScene(scene);
primaryStage.show();
scene.setOnKeyPressed(e -> {
if (e.getCode() == KeyCode.UP)
animation.setRate(animation.getRate() + 0.1);
else if (e.getCode() == KeyCode.DOWN){
animation.setRate(
animation.getRate() > 0 ? animation.getRate() - 0.1 : 0);
}
});
}
}
Related
I would like to know if there is a way to do a FadeTransition from the right or left or even top of bottom kind of like the example here:
Style the label with a text fill set to a linear gradient, and change the stops of the linear gradient in a timeline.
Here's a basic example (click on "Fading Label" to see the animation). A similar approach should work for fading out.
import javafx.animation.KeyFrame;
import javafx.animation.KeyValue;
import javafx.animation.Timeline;
import javafx.application.Application;
import javafx.beans.binding.Bindings;
import javafx.beans.property.DoubleProperty;
import javafx.beans.property.SimpleDoubleProperty;
import javafx.scene.Scene;
import javafx.scene.control.Label;
import javafx.scene.layout.HBox;
import javafx.stage.Stage;
import javafx.util.Duration;
public class TextFade extends Application {
#Override
public void start(Stage primaryStage) {
Label label = new Label("Fading label:");
Label fadingLabel = new Label("text fades in and out");
DoubleProperty startFade = new SimpleDoubleProperty(0);
DoubleProperty endFade = new SimpleDoubleProperty(0);
fadingLabel.styleProperty().bind(Bindings.createStringBinding(() -> String.format(
"-fx-text-fill: linear-gradient(to right, -fx-text-background-color 0%%, -fx-text-background-color %f%%, transparent %f%%, transparent 100%%);",
startFade.get()*100, endFade.get()*100
), startFade, endFade));
HBox root = new HBox(2, label, fadingLabel);
Timeline timeline = new Timeline(
new KeyFrame(Duration.ZERO, new KeyValue(startFade, 0), new KeyValue(endFade, 0)),
new KeyFrame(Duration.seconds(1.0/3.0), new KeyValue(startFade, 0)),
new KeyFrame(Duration.seconds(2.0/3.0), new KeyValue(endFade, 1)),
new KeyFrame(Duration.seconds(1), new KeyValue(startFade, 1)));
label.setOnMouseClicked(e -> timeline.play());
Scene scene = new Scene(root, 400, 400);
primaryStage.setScene(scene);
primaryStage.show();
}
public static void main(String[] args) {
launch(args);
}
}
Here is my following code:
public void start(Stage primaryStage) throws Exception {
Pane root = new Pane();
Scene scene = new Scene(root, 500, 500, Color.RED);
ImageView dice = new ImageView(new Image(getClass().getResourceAsStream("dice.jpeg")));
dice.setX(0);
dice.setY(300);
root.getChildren().add(dice);
scene.setOnKeyPressed(e -> {
if (dice.getX() >= 0 && dice.getX() <= 500 ) {
switch (e.getCode()) {
case RIGHT:
dice.setX(dice.getX() + KEYBOARD_MOVEMENT_DELTA);
break;
case LEFT:
dice.setX(dice.getX() - KEYBOARD_MOVEMENT_DELTA);
break;
}
}
});
primaryStage.setScene(scene);
primaryStage.show();
}
In my code, my image dice can move left and right, but I don't want it to go outside the scene. I want it to not move once it reaches to end of the scene in both left and right. I tried doing it with if statement, but it doesn't work. Is there any way I can stop my image diceto not move out of scene? Any help is appreciated!
You have a couple of problems in your answer. Firstly, the way you check the boundaries. If the condition is met, your keys no longer control the ImageView. Secondly, using dice.getX() in your test condition. It's best if you use dice.getLayoutBounds().getMaxX() and dice.getLayoutBounds().getMinX(). Also, I recommend using scene.getWidth() instead of hard coding the width of the Scene, because the Scene width can be changed. <-(in your posted code).
import java.io.IOException;
import java.io.InputStream;
import java.net.URL;
import java.net.URLConnection;
import javafx.application.Application;
import javafx.scene.Scene;
import javafx.scene.image.Image;
import javafx.scene.image.ImageView;
import javafx.scene.layout.Pane;
import javafx.scene.paint.Color;
import javafx.stage.Stage;
/**
*
* #author blj0011
*/
public class JavaFxTestingGround extends Application {
double KEYBOARD_MOVEMENT_DELTA = 5;
#Override
public void start(Stage primaryStage) throws IOException {
Pane root = new Pane();
Scene scene = new Scene(root, 500, 500, Color.RED);
ImageView dice = new ImageView(createImage("https://cdn.discordapp.com/attachments/250163910454280192/296377451599364107/Untitled.png"));
dice.setFitHeight(100);
dice.setFitWidth(100);
dice.setX(0);
dice.setY(300);
root.getChildren().add(dice);
scene.setOnKeyPressed(e -> {
System.out.println(dice.getLayoutBounds().getMinX() + " : " + dice.getLayoutBounds().getMaxX() + " : " + scene.getWidth());
switch (e.getCode()) {
case RIGHT:
dice.setX(dice.getX() + KEYBOARD_MOVEMENT_DELTA);
break;
case LEFT:
dice.setX(dice.getX() - KEYBOARD_MOVEMENT_DELTA);
break;
}
if (dice.getLayoutBounds().getMinX() < 0)
{
dice.setX(0);
}
else if(dice.getLayoutBounds().getMaxX() > scene.getWidth() )
{
dice.setX(dice.getX() - KEYBOARD_MOVEMENT_DELTA);
}
});
primaryStage.setScene(scene);
primaryStage.show();
}
/**
* #param args the command line arguments
*/
public static void main(String[] args) {
launch(args);
}
Image createImage(String url)
throws IOException {
URLConnection conn = new URL(url).openConnection();
conn.setRequestProperty("User-Agent", "Wget/1.13.4 (linux-gnu)");
try (InputStream stream = conn.getInputStream()) {
return new Image(stream);
}
}
}
I am trying to pause the PathTransition and restart it using the keyboard key P.
And also I am trying to increase and decrease the animation speed using UP/Down keys.
But when I run the code, these buttons dont seem to work. What am I doing wrong?
package exercise_15_29;
import javafx.animation.Animation;
import javafx.animation.PathTransition;
import javafx.animation.Timeline;
import javafx.application.Application;
import javafx.scene.Group;
import javafx.scene.Scene;
import javafx.scene.layout.Pane;
import javafx.scene.paint.Color;
import javafx.scene.shape.Circle;
import javafx.scene.shape.Line;
import javafx.scene.shape.Polygon;
import javafx.scene.input.KeyCode;
import javafx.scene.shape.Rectangle;
import javafx.stage.Stage;
import javafx.util.Duration;
public class Exercise_15_29 extends Application {
Group car = new Group();
Circle wheel1 = new Circle(15,95,5);
Circle wheel2 = new Circle(35,95,5);
Polygon body1 = new Polygon();
Rectangle body2 = new Rectangle(0.0,80.0,50,10);
Line path = new Line(0,90,500,90);
int speed = 4000;
boolean play = true;
#Override
public void start(Stage primaryStage) {
body1.getPoints().addAll(new Double[]{
10.0, 80.0,
20.0, 70.0,
30.0, 70.0,
40.0, 80.0
});
body1.setFill(Color.BLUE);
body2.setFill(Color.SKYBLUE);
path.setVisible (false);
car.getChildren().addAll(wheel1,wheel2,body1,body2);
PathTransition pt = new PathTransition();
pt.setDuration(Duration.millis(speed));
pt.setPath(path);
pt.setNode(car);
pt.setCycleCount(Timeline.INDEFINITE);
pt.setAutoReverse(false);
pt.play();
Pane root = new Pane();
root.getChildren().add(car);
root.getChildren().add(path);
Scene scene = new Scene(root, 500, 100);
primaryStage.setTitle("Hello World!");
primaryStage.setScene(scene);
primaryStage.show();
root.setOnKeyTyped(e -> {
if(e.getCode() == KeyCode.P) {
if(play == true)
pt.stop();
else
pt.play();
play = !play;
}
});
root.setOnKeyPressed(e -> {
if(e.getCode() == KeyCode.UP)
pt.setDuration(Duration.millis(++speed));
else if(e.getCode() == KeyCode.DOWN)
pt.setDuration(Duration.millis(--speed));
});
}
public static void main(String[] args) {
launch(args);
}
}
this code works for me:
import javafx.animation.PathTransition;
import javafx.animation.Timeline;
import javafx.application.Application;
import javafx.scene.Group;
import javafx.scene.Scene;
import javafx.scene.input.KeyCode;
import javafx.scene.input.KeyEvent;
import javafx.scene.layout.Pane;
import javafx.scene.paint.Color;
import javafx.scene.shape.Circle;
import javafx.scene.shape.Line;
import javafx.scene.shape.Polygon;
import javafx.scene.shape.Rectangle;
import javafx.stage.Stage;
import javafx.util.Duration;
public class Exercise_15_29 extends Application {
Group car = new Group();
Circle wheel1 = new Circle(15, 95, 5);
Circle wheel2 = new Circle(35, 95, 5);
Polygon body1 = new Polygon();
Rectangle body2 = new Rectangle(0.0, 80.0, 50, 10);
Line path = new Line(0, 90, 500, 90);
int speed = 4000;
boolean play = true;
public static void main(String[] args) {
launch(args);
}
#Override
public void start(Stage primaryStage) {
body1.getPoints().addAll(new Double[]{
10.0, 80.0,
20.0, 70.0,
30.0, 70.0,
40.0, 80.0
});
body1.setFill(Color.BLUE);
body2.setFill(Color.SKYBLUE);
path.setVisible(false);
car.getChildren().addAll(wheel1, wheel2, body1, body2);
PathTransition pt = new PathTransition();
pt.setDuration(Duration.millis(speed));
pt.setPath(path);
pt.setNode(car);
pt.setCycleCount(Timeline.INDEFINITE);
pt.setAutoReverse(false);
pt.play();
Pane root = new Pane();
root.getChildren().add(car);
root.getChildren().add(path);
Scene scene = new Scene(root, 500, 100);
scene.addEventFilter(KeyEvent.KEY_PRESSED, e -> {
System.out.println("P");
if (e.getCode() == KeyCode.P) {
if (play)
pt.stop();
else
pt.play();
play = !play;
}
});
primaryStage.setTitle("Hello World!");
primaryStage.setScene(scene);
primaryStage.show();
// root.addEventFilter(KeyEvent.KEY_PRESSED, e -> {
// if (e.getCode() == KeyCode.P) {
// if (play == true)
// pt.stop();
// else
// pt.play();
// play = !play;
// }
// });
scene.setOnKeyPressed(e -> {
System.out.println("UP");
if (e.getCode() == KeyCode.UP)
pt.setDuration(Duration.millis(++speed));
else if (e.getCode() == KeyCode.DOWN)
pt.setDuration(Duration.millis(--speed));
});
// root.setOnKeyPressed(e -> {
// if(e.getCode() == KeyCode.UP)
// pt.setDuration(Duration.millis(++speed));
// else if(e.getCode() == KeyCode.DOWN)
// pt.setDuration(Duration.millis(--speed));
// });
}
}
I changed KeyTyped event to KEY_PRESSED (I recommend this), and also used scene.addEventFilter instead of root.setOnKeyPressed according to https://stackoverflow.com/a/24126049/3291867 and at last you can't change speed of car, you can't change Animation duration after or on playing it (as I know), you can use AnimationTimer for this.
I'an busy with some demo, drawing some lines in a scroll window. So far so good, but now it's possible to draw lines on the menuBar, which should not be possible of course. See code below. Please help!
This is what happens:
See output here
The wrong code:
package Example12a;
import javafx.application.Application;
import javafx.event.ActionEvent;
import javafx.event.EventHandler;
import javafx.scene.Scene;
import javafx.scene.control.Menu;
import javafx.scene.control.MenuBar;
import javafx.scene.control.MenuItem;
import javafx.scene.control.ScrollPane;
import javafx.scene.layout.BorderPane;
import javafx.scene.layout.Pane;
import javafx.scene.shape.Line;
import javafx.stage.Stage;
public class Example12a extends Application {
public static void main(String[] args) {
launch(args);
}
private Line curLine;
#Override
public void start(Stage stage) throws Exception {
Pane drawingPane = new Pane();
BorderPane theBorderPane = new BorderPane();
drawingPane.setPrefSize(800, 800);
drawingPane.setMaxSize(Double.MAX_VALUE, Double.MAX_VALUE);
MenuBar menuBar = new MenuBar();
// --- Menu File
Menu menuFile = new Menu("File");
MenuItem add = new MenuItem("Save");
add.setOnAction(new EventHandler<ActionEvent>() {
#Override
public void handle(ActionEvent t) {
System.out.println("Save");
}
});
menuFile.getItems().addAll(add);
//yOffset = (int)menuBar.getHeight();
Menu menuEdit = new Menu("Edit");
Menu menuView = new Menu("View");
menuBar.getMenus().addAll(menuFile, menuEdit, menuView);
theBorderPane.setTop(menuBar);
ScrollPane scrollPane = new ScrollPane(theBorderPane);
scrollPane.setPrefSize(300, 300);
scrollPane.setMaxSize(Double.MAX_VALUE, Double.MAX_VALUE);
scrollPane.setFitToWidth(true);
scrollPane.setFitToHeight(true);
scrollPane.setStyle("-fx-focus-color: transparent;");
theBorderPane.setOnMousePressed(event -> {
if (!event.isPrimaryButtonDown()) {
return;
}
curLine = new Line(
event.getX(), event.getY(),
event.getX(), event.getY()
);
theBorderPane.getChildren().add(curLine);
});
theBorderPane.setOnMouseDragged(event -> {
if (!event.isPrimaryButtonDown()) {
return;
}
if (curLine == null) {
return;
}
curLine.setEndX(event.getX());
curLine.setEndY(event.getY());
double mx = Math.max(curLine.getStartX(), curLine.getEndX());
double my = Math.max(curLine.getStartY(), curLine.getEndY());
if (mx > theBorderPane.getMinWidth()) {
theBorderPane.setMinWidth(mx);
}
if (my > theBorderPane.getMinHeight()) {
theBorderPane.setMinHeight(my);
}
});
theBorderPane.setOnMouseReleased(event -> curLine = null);
theBorderPane.setCenter(drawingPane);
Scene scene = new Scene(scrollPane);
stage.setMinWidth(100);
stage.setMinHeight(100);
stage.setScene(scene);
stage.show();
}
}
Fixed your layout.
What i did was:
The BorderPane is now your root Pane.
The ScrollPane is the center of the BorderPane and its content is the drawingPane.
The MenuBar is still the the Top of the BorderPane.
I also changed the Mouse Events from borderPane to drawingPane and the lines are added to the drawingPane instead of the borderPane.
So its working fine.
import javafx.application.Application;
import javafx.event.ActionEvent;
import javafx.event.EventHandler;
import javafx.scene.Scene;
import javafx.scene.control.Menu;
import javafx.scene.control.MenuBar;
import javafx.scene.control.MenuItem;
import javafx.scene.control.ScrollPane;
import javafx.scene.layout.BorderPane;
import javafx.scene.layout.Pane;
import javafx.scene.shape.Line;
import javafx.stage.Stage;
public class Example12a extends Application {
public static void main(String[] args) {
launch(args);
}
private Line curLine;
#Override
public void start(Stage stage) throws Exception {
Pane drawingPane = new Pane();
BorderPane theBorderPane = new BorderPane();
drawingPane.setPrefSize(800, 800);
drawingPane.setMaxSize(Double.MAX_VALUE, Double.MAX_VALUE);
MenuBar menuBar = new MenuBar();
// --- Menu File
Menu menuFile = new Menu("File");
MenuItem add = new MenuItem("Save");
add.setOnAction(new EventHandler<ActionEvent>() {
#Override
public void handle(ActionEvent t) {
System.out.println("Save");
}
});
menuFile.getItems().addAll(add);
//yOffset = (int)menuBar.getHeight();
Menu menuEdit = new Menu("Edit");
Menu menuView = new Menu("View");
menuBar.getMenus().addAll(menuFile, menuEdit, menuView);
theBorderPane.setTop(menuBar);
ScrollPane scrollPane = new ScrollPane(drawingPane);
scrollPane.setPrefSize(300, 300);
scrollPane.setMaxSize(Double.MAX_VALUE, Double.MAX_VALUE);
scrollPane.setFitToWidth(true);
scrollPane.setFitToHeight(true);
scrollPane.setStyle("-fx-focus-color: transparent;");
drawingPane.setOnMousePressed(event -> {
if (!event.isPrimaryButtonDown()) {
return;
}
curLine = new Line(
event.getX(), event.getY(),
event.getX(), event.getY()
);
drawingPane.getChildren().add(curLine);
});
drawingPane.setOnMouseDragged(event -> {
if (!event.isPrimaryButtonDown()) {
return;
}
if (curLine == null) {
return;
}
curLine.setEndX(event.getX());
curLine.setEndY(event.getY());
double mx = Math.max(curLine.getStartX(), curLine.getEndX());
double my = Math.max(curLine.getStartY(), curLine.getEndY());
if (mx > drawingPane.getMinWidth()) {
drawingPane.setMinWidth(mx);
}
if (my > drawingPane.getMinHeight()) {
drawingPane.setMinHeight(my);
}
});
theBorderPane.setOnMouseReleased(event -> curLine = null);
theBorderPane.setCenter(scrollPane);
Scene scene = new Scene(theBorderPane);
stage.setMinWidth(100);
stage.setMinHeight(100);
stage.setScene(scene);
stage.show();
}
}
Note:
if your trying to make a Drawing Programm I would prevere to Render all Lines in a Canvas instead of using the Line class. The Canvas is much faster with many Lines.
I would like to fade in and fadeout a circle in a javafx canvas.
I can move a circle from one part of the screen to another but i just can't seem to get this object to fade in and out.
Below is the code i used for moving a circle from one part of the screen to the other
public class AnimatedCircleOnCanvas extends Application {
public static final double W = 200; // canvas dimensions.
public static final double H = 200;
public static final double D = 20; // diameter.
#Override public void start(Stage stage) {
DoubleProperty x = new SimpleDoubleProperty();
DoubleProperty y = new SimpleDoubleProperty();
Timeline timeline = new Timeline(
new KeyFrame(Duration.seconds(0),
new KeyValue(x, 0),
new KeyValue(y, 0)
),
new KeyFrame(Duration.seconds(3),
new KeyValue(x, W - D),
new KeyValue(y, H - D)
)
);
timeline.setAutoReverse(true);
timeline.setCycleCount(Timeline.INDEFINITE);
final Canvas canvas = new Canvas(W, H);
AnimationTimer timer = new AnimationTimer() {
#Override
public void handle(long now) {
GraphicsContext gc = canvas.getGraphicsContext2D();
gc.setFill(Color.CORNSILK);
gc.fillRect(0, 0, W, H);
gc.setFill(Color.FORESTGREEN);
gc.fillOval(
x.doubleValue(),
y.doubleValue(),
D,
D
);
}
};
stage.setScene(
new Scene(
new Group(
canvas
)
)
);
stage.show();
timer.start();
timeline.play();
}
public static void main(String[] args) { launch(args); }
}
Your help is greatly appreciated
Animate a DoubleProperty for the opacity in values ranging from 1 to 0 in exactly the same way as you animated the properties for x and y, and then use Color.deriveColor(...) to set the color based on the changing property:
import javafx.animation.AnimationTimer;
import javafx.animation.KeyFrame;
import javafx.animation.KeyValue;
import javafx.animation.Timeline;
import javafx.application.Application;
import javafx.beans.property.DoubleProperty;
import javafx.beans.property.SimpleDoubleProperty;
import javafx.scene.Group;
import javafx.scene.Scene;
import javafx.scene.canvas.Canvas;
import javafx.scene.canvas.GraphicsContext;
import javafx.scene.paint.Color;
import javafx.stage.Stage;
import javafx.util.Duration;
public class FadeCircleOnCanvas extends Application {
public static final double W = 200; // canvas dimensions.
public static final double H = 200;
public static final double D = 20; // diameter.
#Override public void start(Stage stage) {
DoubleProperty opacity = new SimpleDoubleProperty();
Timeline timeline = new Timeline(
new KeyFrame(Duration.seconds(0),
new KeyValue(opacity, 1)
),
new KeyFrame(Duration.seconds(3),
new KeyValue(opacity, 0)
)
);
timeline.setAutoReverse(true);
timeline.setCycleCount(Timeline.INDEFINITE);
final Canvas canvas = new Canvas(W, H);
AnimationTimer timer = new AnimationTimer() {
#Override
public void handle(long now) {
GraphicsContext gc = canvas.getGraphicsContext2D();
gc.setFill(Color.CORNSILK);
gc.fillRect(0, 0, W, H);
gc.setFill(Color.FORESTGREEN.deriveColor(0, 1, 1, opacity.get()));
gc.fillOval(
W/2,
H/2,
D,
D
);
}
};
stage.setScene(
new Scene(
new Group(
canvas
)
)
);
stage.show();
timer.start();
timeline.play();
}
public static void main(String[] args) { launch(args); }
}
In general, I prefer not to use a Canvas for this, and just to use Shape instances placed in a Pane. Then you can directly change the pre-defined properties of the Shape, or in many cases use specific pre-defined animations (TranslateTransition or FadeTransition for example).
Here's the same example using this technique:
import javafx.animation.FadeTransition;
import javafx.animation.Timeline;
import javafx.application.Application;
import javafx.scene.Scene;
import javafx.scene.layout.Pane;
import javafx.scene.paint.Color;
import javafx.scene.shape.Circle;
import javafx.stage.Stage;
import javafx.util.Duration;
public class FadeCircleOnCanvas extends Application {
public static final double W = 200; // canvas dimensions.
public static final double H = 200;
public static final double D = 20; // diameter.
#Override public void start(Stage stage) {
Circle circle = new Circle(W/2, H/2, D, Color.FORESTGREEN);
FadeTransition fade = new FadeTransition(Duration.seconds(3), circle);
fade.setFromValue(1);
fade.setToValue(0);
fade.setAutoReverse(true);
fade.setCycleCount(Timeline.INDEFINITE);
Pane pane = new Pane(circle);
stage.setScene(
new Scene(
pane, W, H, Color.CORNSILK
)
);
stage.show();
fade.play();
}
public static void main(String[] args) { launch(args); }
}