JavaFX animations using loops? - java

I'm trying to build a space invaders like game.
I've drawn a square and I want to move it down incrementally by using a loop and thread.sleep. However, the square just gets drawn immediately. I understand there are animation paths that could be used but I want to stay low level and just use a coordinate system.
Is there a way of making a timeline animation by using a loop like this?
package com.company;
import javafx.application.Application;
import javafx.stage.Stage;
import javafx.scene.*;
import javafx.scene.layout.*;
import javafx.scene.shape.Rectangle;
public class Main extends Application {
public static void main(String[] args) {
// write your code here
launch(args);
}
public void start(Stage myStage) throws InterruptedException {
myStage.setTitle("space invaders");
Pane rootNode= new Pane();
Scene myScene=new Scene(rootNode, 400, 800);
myStage.setScene(myScene);
Rectangle r = new Rectangle();
myStage.show();
rootNode.getChildren().add(r);
r.setX(50);
r.setY(50);
r.setWidth(20);
r.setHeight(20);
for (int i = 0; i < 500; i++){
Thread.sleep(2000);
r.setTranslateY(i);
}
}
}

This is a terrible implementation. I would probably use AnimationTimer. This is done with Timeline. It's basically moving right or left. If you hit the right or left bound, drop then move in the opposite direction.
import java.util.ArrayList;
import java.util.List;
import java.util.Optional;
import javafx.animation.KeyFrame;
import javafx.animation.PauseTransition;
import javafx.animation.Timeline;
import javafx.application.Application;
import javafx.event.ActionEvent;
import javafx.scene.Scene;
import javafx.scene.control.Button;
import javafx.scene.layout.Pane;
import javafx.scene.layout.StackPane;
import javafx.scene.layout.VBox;
import javafx.scene.paint.Color;
import javafx.scene.shape.Rectangle;
import javafx.stage.Stage;
import javafx.util.Duration;
/**
*
* #author blj0011
*/
public class JavaFXApplication343 extends Application
{
int invaderWidth = 30;
int invaderHeight = 10;
int gapBetweenInvaderX = 5;
int gapBetweenInvaderY = 5;
int locationTrackerX;
int locationTrackerY;
int screenWidth = 300;
int screenHeight = 400;
double timeBetweenFrames = .25;
boolean direction = true;
Timeline timeline;
#Override
public void start(Stage primaryStage)
{
Pane pane = new Pane();
locationTrackerX = (screenWidth - (invaderWidth * 6 + gapBetweenInvaderX * 5)) / 2;
locationTrackerY = (screenHeight - (invaderHeight * 6 + gapBetweenInvaderY * 5)) / 7;
List<Rectangle> invaders = new ArrayList();
for (int i = 0; i < 36; i++) {
Rectangle rectangle = new Rectangle(locationTrackerX, locationTrackerY, invaderWidth, invaderHeight);
rectangle.setFill(Color.YELLOW);
invaders.add(rectangle);
System.out.println(locationTrackerX);
locationTrackerX += invaderWidth + gapBetweenInvaderX;
if ((i + 1) % 6 == 0) {
locationTrackerX = (screenWidth / 2) - ((invaderWidth * 6 + gapBetweenInvaderX * 5) / 2);
locationTrackerY += invaderHeight + gapBetweenInvaderY;
}
}
timeline = new Timeline(new KeyFrame(Duration.seconds(timeBetweenFrames), (event) -> {
//Check to see if invader hits bounds
Optional<Rectangle> hitRightOptional = invaders.stream().filter(invader -> invader.getBoundsInLocal().getMaxX() >= pane.getWidth()).findFirst();
Optional<Rectangle> hitLeftOptional = invaders.stream().filter(invader -> invader.getBoundsInLocal().getMinX() <= 0).findFirst();
//Move invaders
if (hitRightOptional.isPresent()) {
invaders.forEach((tempInvader) -> tempInvader.setY(tempInvader.getY() + 10));
timeline.stop();
PauseTransition pause = new PauseTransition(Duration.seconds(timeBetweenFrames));
pause.setOnFinished((pauseEvent) -> {
invaders.forEach(invader -> invader.setX(invader.getX() - 10));
timeline.play();
});
pause.play();
direction = false;
}
else if (hitLeftOptional.isPresent()) {
invaders.forEach((tempInvader) -> tempInvader.setY(tempInvader.getY() + 10));
timeline.stop();
PauseTransition pause = new PauseTransition(Duration.seconds(timeBetweenFrames));
pause.setOnFinished((pauseEvent) -> {
invaders.forEach(invader -> invader.setX(invader.getX() + 10));
timeline.play();
});
pause.play();
direction = true;
}
else {
if (direction) {
invaders.forEach(invader -> invader.setX(invader.getX() + 10));
}
else {
invaders.forEach(invader -> invader.setX(invader.getX() - 10));
}
}
}));
timeline.setCycleCount(Timeline.INDEFINITE);
Button btn = new Button();
btn.setText("Start Game");
btn.setOnAction((ActionEvent event) -> {
timeline.play();
btn.setDisable(true);
});
pane.getChildren().addAll(invaders);
pane.setPrefSize(screenWidth, screenHeight);
VBox root = new VBox(pane, new StackPane(btn));
Scene scene = new Scene(root, screenWidth, screenHeight);
primaryStage.setTitle("Hello World!");
primaryStage.setScene(scene);
primaryStage.show();
}
/**
* #param args the command line arguments
*/
public static void main(String[] args)
{
launch(args);
}
}

Solved :
Task task = new Task<Void>() {
#Override public Void call() throws InterruptedException {
for (int i = 1; i <= 800; i=i+10) {
Thread.sleep(25);
//updateProgress(i, max);
r.setTranslateY(i);
}
return null;
}
};
new Thread(task).start();

Related

How to update part of BufferedImage and display it with javafx

I have an image. At the beginning I want the image to be displayed with only the first frame. On the keyboard with 'u' key pressed, drink water and load the second frame. If I understand correctly the whole image should be loaded at the beginning. And then I need to set new y.
How to complete this task? What is the right way to display this image?
My code in:
package testDesktopUi;
import javafx.application.Application;
import javafx.embed.swing.SwingFXUtils;
import javafx.scene.Scene;
import javafx.scene.image.Image;
import javafx.scene.image.ImageView;
import javafx.scene.input.KeyCode;
import javafx.scene.layout.HBox;
import javafx.stage.Stage;
import javax.imageio.ImageIO;
import java.awt.image.BufferedImage;
import java.io.File;
import java.io.IOException;
public class DesktopUi extends Application {
private static int y = 0;
private static BufferedImage bufferedImage;
private static final ImageView imageView = new ImageView();
private static final HBox root = new HBox();
#Override
public void start(Stage primaryStage) throws IOException {
String path = "Bottle.png";
bufferedImage = ImageIO.read(new File(path)).getSubimage(0, 0, 32, 32);
Image image = SwingFXUtils.toFXImage(bufferedImage, null);
imageView.setImage(image);
root.getChildren().add(imageView);
Scene scene = new Scene(root);
scene.setOnKeyPressed(key -> {
if (key.getCode() == KeyCode.U) {
System.out.println("u pressed");
updateImage(bufferedImage);
System.out.println(y);
}
});
primaryStage.setScene(scene);
primaryStage.show();
}
private static void updateImage(BufferedImage bufferedImage) {
int maxHeight = 352;
if (y + 32 >= maxHeight) {
y = 0;
} else {
y += 32;
}
//?? bufferedImage.getSubimage(0, y, 32, 32);
}
}
If you're using JavaFX, stick just to the JavaFX image API: there is no need to first load an AWT BufferedImage and convert it to a JavaFX image.
To display portions of an image, you can create an ImageView from the image and set the ImageView's viewport.
import javafx.application.Application;
import javafx.geometry.Rectangle2D;
import javafx.scene.Scene;
import javafx.scene.image.Image;
import javafx.scene.image.ImageView;
import javafx.scene.input.KeyCode;
import javafx.scene.layout.HBox;
import javafx.stage.Stage;
public class DesktopUi extends Application {
private int y = 0;
private final ImageView imageView = new ImageView();
private final HBox root = new HBox();
#Override
public void start(Stage primaryStage) {
String path = "Bottle.png";
// Assumes Bottle.png is in the same package as the current class:
Image image = new Image(getClass().getResource("Bottle.png").toExternalForm());
imageView.setImage(image);
// display only a portion of the image:
imageView.setViewport(new Rectangle2D(0, y, 32, 32));
root.getChildren().add(imageView);
Scene scene = new Scene(root);
scene.setOnKeyPressed(key -> {
if (key.getCode() == KeyCode.U) {
System.out.println("u pressed");
System.out.println(y);
updateImage();
}
});
primaryStage.setScene(scene);
primaryStage.show();
}
private void updateImage() {
// update y
int maxHeight = 352;
if (y + 32 >= maxHeight) {
y = 0;
} else {
y += 32;
}
// update portion of image displayed
imageView.setViewport(new Rectangle2D(0, y, 32, 32));
}
public static void main(String[] args) {
Application.launch(args);
}
}
Note this also lends itself nicely to animations:
#Override
public void start(Stage primaryStage) {
int numSprites = 11 ;
Image image = new Image(getClass().getResource("Bottle.png").toExternalForm());
imageView.setImage(image);
IntegerProperty spriteIndex = new SimpleIntegerProperty();
spriteIndex.addListener((obs, oldIndex, newIndex) -> System.out.println(newIndex));
imageView.viewportProperty().bind(Bindings.createObjectBinding(
() -> new Rectangle2D(0, spriteIndex.get() * 32 , 32, 32),
spriteIndex
));
Timeline timeline = new Timeline(new KeyFrame(Duration.seconds(5),
new KeyValue(spriteIndex, numSprites - 1)));
timeline.setCycleCount(Animation.INDEFINITE);
timeline.play();
root.getChildren().add(imageView);
Scene scene = new Scene(root);
primaryStage.setScene(scene);
primaryStage.show();
}

Setting a node in a specific location Javafx [closed]

Closed. This question needs details or clarity. It is not currently accepting answers.
Want to improve this question? Add details and clarify the problem by editing this post.
Closed 2 years ago.
Improve this question
I'm trying to make a Scrabble game, but I'm having trouble adding the tiles to match exactly to the board coordinates. SO far I'm having trouble figuring that out. Here is my code
import javafx.application.Application;
import javafx.scene.Cursor;
import javafx.scene.Group;
import javafx.scene.Parent;
import javafx.scene.Scene;
import javafx.scene.layout.Pane;
import javafx.stage.Stage;
public class Main extends Application {
public static final int TILE_SIZE = 40;
private static final int W = 800;
private static final int H = 600;
private static final int X_TILES = 15;
private static final int Y_TILES = 15;
private Tile[][] grid = new Tile[X_TILES][Y_TILES];
private GameTile[][] bag = new GameTile[19][19];
double orgSceneX, orgSceneY;
double orgTranslateX, orgTranslateY;
Group tileGroup = new Group();
Group pieceGroup = new Group();
private Scene scene;
static public void main(String[] args) {
launch(args);
}
#Override
public void start(Stage stage) throws Exception {
scene = new Scene(createContent());
stage.setScene(scene);
stage.show();
}
private Parent createContent() {
Pane root = new Pane();
root.setPrefSize(W, H);
root.getChildren().addAll(tileGroup, pieceGroup);
//creates board tiles
for (int x = 0; x < X_TILES; x++) {
for (int y = 0; y < Y_TILES; y++) {
Tile tile = new Tile(x, y);
grid[y][x] = tile;
tileGroup.getChildren().add(tile);
}
}
int z = 0;
//creates bag tiles
for (int y = 0; y < 3; y++) {
for (int x = 15; x < 18; x++) {
GameTile bagTile = new GameTile(x, y);
bagTile.setCursor(Cursor.HAND);
bagTile.setOnMousePressed(e -> {
orgSceneX = e.getSceneX();
orgSceneY = e.getSceneY();
orgTranslateX = ((GameTile) (e.getSource())).getTranslateX();
orgTranslateY = ((GameTile) (e.getSource())).getTranslateY();
});
bagTile.setOnMouseDragged(e -> {
double offsetX = e.getSceneX() - orgSceneX;
double offsetY = e.getSceneY() - orgSceneY;
double newTranslateX = orgTranslateX + offsetX;
double newTranslateY = orgTranslateY + offsetY;
((GameTile) (e.getSource())).setTranslateX((int)(newTranslateX));
((GameTile) (e.getSource())).setTranslateY((int)newTranslateY);
});
bagTile.setOnMouseReleased(e -> {
});
bag[x][y] = bagTile;
root.getChildren().add(bagTile);
z++;
}
}
return root;
}
}
I tried implementing a mouse handler, but so far I keep failing to understand how to make the exact coordinates match the board underneath the draggable tiles.
One way to do this is to use ideas from this answer. I altered the code so that it sets the node that is being dragged invisible and shows a snapshot of that node being dragged by the mouse.
You could probably use Label instead of Button for the Tiles.
import javafx.application.Application;
import javafx.geometry.Insets;
import javafx.scene.Scene;
import javafx.scene.SnapshotParameters;
import javafx.scene.control.Button;
import javafx.scene.input.*;
import javafx.scene.layout.GridPane;
import javafx.scene.layout.HBox;
import javafx.scene.layout.StackPane;
import javafx.scene.layout.VBox;
import javafx.stage.Stage;
/**
* Demonstrates a drag-and-drop feature.
*/
public class App extends Application
{
#Override
public void start(Stage stage)
{
//Source Buttons.
final Button boat1 = new Button("boat1");
final Button boat2 = new Button("boat2");
final Button boat3 = new Button("boat3");
final Button boat4 = new Button("boat4");
//Adding OnDragDetected to source Buttons.
setOnDragDetected(boat1);
setOnDragDetected(boat2);
setOnDragDetected(boat3);
setOnDragDetected(boat4);
//Adding onDragDone to source Buttons.
setOnDragDone(boat1);
setOnDragDone(boat2);
setOnDragDone(boat3);
setOnDragDone(boat4);
//Creating GridPane
GridPane gridPane = new GridPane();
gridPane.setVgap(5);
gridPane.setHgap(5);
gridPane.setPadding(new Insets(5, 5, 5, 5));
gridPane.setStyle("-fx-background-color: black;");
//Adding StackPane to every Cell in the GridPane and Adding the Target Events to each StackPane.
for (int i = 0; i < 6; i++) {
StackPane stackPane = new StackPane();
stackPane.setPrefSize(150, 50);
stackPane.setStyle("-fx-background-color: yellow;");
setOnDragOver(stackPane);
setOnDragEntered(stackPane);
setOnDragExited(stackPane);
setOnDragDropped(stackPane);
gridPane.add(stackPane, i / 3, i % 3);
}
HBox root = new HBox(new VBox(boat1, boat2, boat3, boat4), gridPane);
stage.setTitle("Hello Drag And Drop");
Scene scene = new Scene(root, 400, 200);
stage.setScene(scene);
stage.show();
}
public static void main(String[] args)
{
Application.launch(args);
}
//source events handlers
public void setOnDragDetected(Button source)
{
source.setOnDragDetected((MouseEvent event) -> {
/* drag was detected, start drag-and-drop gesture*/
System.out.println("onDragDetected");
/* allow any transfer mode */
Dragboard db = source.startDragAndDrop(TransferMode.ANY);
db.setDragView(source.snapshot(new SnapshotParameters(), null));
source.setVisible(false);
/* put a string on dragboard */
ClipboardContent content = new ClipboardContent();
content.putString(source.getText());
db.setContent(content);
event.consume();
});
}
public void setOnDragDone(Button source)
{
source.setOnDragDone((DragEvent event) -> {
/* the drag-and-drop gesture ended */
System.out.println("onDragDone");
/* if the data was successfully moved, clear it */
// if (event.getTransferMode() == TransferMode.MOVE) {
// source.setText("");
// }
event.consume();
});
}
//target event handlers
public void setOnDragOver(StackPane target)
{
target.setOnDragOver((DragEvent event) -> {
/* data is dragged over the target */
System.out.println("onDragOver");
/* accept it only if it is not dragged from the same node
* and if it has a string data */
if (event.getGestureSource() != target
&& event.getDragboard().hasString()) {
/* allow for both copying and moving, whatever user chooses */
event.acceptTransferModes(TransferMode.COPY_OR_MOVE);
}
event.consume();
});
}
public void setOnDragEntered(StackPane target)
{
target.setOnDragEntered((DragEvent event) -> {
/* the drag-and-drop gesture entered the target */
System.out.println("onDragEntered");
/* show to the user that it is an actual gesture target */
if (event.getGestureSource() != target
&& event.getDragboard().hasString()) {
target.setStyle("-fx-background-color: green;");
}
event.consume();
});
}
public void setOnDragExited(StackPane target)
{
target.setOnDragExited((DragEvent event) -> {
/* mouse moved away, remove the graphical cues */
target.setStyle("-fx-background-color: yellow;");
event.consume();
});
}
public void setOnDragDropped(StackPane target)
{
target.setOnDragDropped((DragEvent event) -> {
/* data dropped */
System.out.println("onDragDropped");
/* if there is a string data on dragboard, read it and use it */
Dragboard db = event.getDragboard();
boolean success = false;
if (db.hasString()) {
//target.setText(db.getString());
Button tempBoat = new Button(db.getString());
tempBoat.setMaxSize(Double.MAX_VALUE, Double.MAX_VALUE);
target.getChildren().clear();
target.getChildren().add(tempBoat);
success = true;
}
/* let the source know whether the string was successfully
* transferred and used */
event.setDropCompleted(success);
event.consume();
});
}
}

Thread stops updating display after a while

I am trying to do a neural network training visualization with JavaFX 8. The network cells are shown as dots changing their color depending on the output value. The calculations and the drawing are done in a thread that can be started or stopped by clicking a button. For a while everything works fine but after a number of iterations the display is no longer updated.
What needs to be done to reliably update the display?
I am using JRE version 1.8.0_45.
Here's a simplified version of my code:
import javafx.application.*;
import javafx.concurrent.Task;
import javafx.geometry.Insets;
import javafx.stage.Stage;
import javafx.scene.control.Button;
import javafx.scene.control.ScrollPane;
import javafx.scene.layout.BorderPane;
import javafx.scene.layout.GridPane;
import javafx.scene.paint.Color;
import javafx.scene.*;
import javafx.scene.canvas.Canvas;
import javafx.scene.canvas.GraphicsContext;
public class TestView extends Application {
public static final int SCENE_WIDTH = 1000;
public static final int SCENE_HEIGHT = 800;
public static final int BUTTON_PANEL_HEIGHT = 80;
private Canvas canvas;
private GraphicsContext gc;
private Task<Void> task = null;
public static void main(String[] args) {
launch(args);
}
#Override
public void start(Stage primaryStage) throws Exception {
primaryStage.setTitle("MLP");
BorderPane borderPane = new BorderPane();
canvas = new Canvas( SCENE_WIDTH, 3*SCENE_HEIGHT-BUTTON_PANEL_HEIGHT );
gc = canvas.getGraphicsContext2D();
borderPane.setCenter(new ScrollPane(canvas));
GridPane buttonPanel = new GridPane();
Button buttonTrain = new Button("Train");
buttonTrain.setMinWidth(SCENE_WIDTH/2);
buttonPanel.setPadding(new Insets(0, 0, 0, 0));
buttonPanel.add(buttonTrain, 1, 0);
borderPane.setBottom(buttonPanel);
buttonTrain.setOnMouseClicked( e -> {
if (task != null) {
task.cancel();
task = null;
}
else {
task = new Task<Void>() {
#Override
protected Void call() throws Exception {
for (int i = 1; i <= 10000000; i++) {
if (isCancelled()) {
break;
}
// dummy calculation
doSomeStuff();
// dummy graphics update
gc.setFill(Color.GREEN);
gc.fillOval(50, 50, 20, 20);
gc.clearRect(200, 10, 200, 100);
gc.setFill(Color.BLACK);
gc.fillText("" + i, 200, 50);
}
return null;
}
};
new Thread(task).start();
}
});
Scene scene = new Scene( borderPane, SCENE_WIDTH, SCENE_HEIGHT );
primaryStage.setScene(scene);
primaryStage.show();
}
private double doSomeStuff() {
double r = 0.5;
for ( int i = 0; i < 10000; i++ ) {
r = Math.sin(r);
}
return r;
}
}

JavaFx how to move the window position with Timeline

I have this Stage and when im showing it . I want it to display at bottom of the screen and then go up to the center screen.
just like a notification .
my code
try {
Stage window = new Stage();
window.initModality(Modality.APPLICATION_MODAL);
window.setTitle(Message);
AnchorPane layout = (AnchorPane) FXMLLoader.load(Notification.class.getResource("window.fxml"));
Scene scene = new Scene(layout);
window.setScene(scene);
window.showAndWait();
} catch (Exception e) {
System.out.println(e);
}
Create a property specifically for the Timeline, and then add a listener to it:
double startPos = ... ;
double endPos = ... ;
DoubleProperty y = new SimpleDoubleProperty(startPos);
Timeline timeline = new Timeline(new KeyFrame(Duration.seconds(1), new KeyValue(y, endPos)));
y.addListener((obs, oldValue, newValue) ->
window.setY(newValue.doubleValue()));
timeline.play();
SSCCE:
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.geometry.Rectangle2D;
import javafx.scene.Scene;
import javafx.scene.control.Button;
import javafx.scene.control.Label;
import javafx.scene.layout.StackPane;
import javafx.scene.paint.Color;
import javafx.stage.Popup;
import javafx.stage.Screen;
import javafx.stage.Stage;
import javafx.util.Duration;
public class SlidingNotificationWindow extends Application {
#Override
public void start(Stage primaryStage) {
Button button = new Button("Show notification");
button.setOnAction(e -> {
Popup window = new Popup();
StackPane content = new StackPane(new Label("Notification"));
content.setStyle("-fx-background-color: aquamarine; -fx-padding: 40;");
content.setOnMouseClicked(evt -> window.hide());
window.getContent().add(content);
window.setWidth(120);
window.setHeight(75);
Rectangle2D primaryScreenBounds = Screen.getPrimary().getVisualBounds();
double startPos = primaryScreenBounds.getMaxY();
double endPos = 2*primaryScreenBounds.getMinY()/3 + primaryScreenBounds.getMaxY() / 3 ;
DoubleProperty y = new SimpleDoubleProperty(startPos);
Timeline timeline = new Timeline(new KeyFrame(Duration.seconds(1), new KeyValue(y, endPos)));
y.addListener((obs, oldValue, newValue) ->
window.setY(newValue.doubleValue()));
timeline.play();
window.setX(primaryScreenBounds.getMaxX() - 120);
window.show(primaryStage);
});
StackPane root = new StackPane(button);
primaryStage.setScene(new Scene(root, 350, 120));
primaryStage.show();
}
public static void main(String[] args) {
launch(args);
}
}
You need to add a Timer where the window will be moved. This is because the Window/Stage only have ReadOnlyProperties for X and Y values, so you cannot make a simple Timeline Animation with Keyframes.
You need to play a bit with the height and width of the primaryScreenBounds, so it will fit your needs.
import javafx.animation.AnimationTimer;
import javafx.application.Application;
import javafx.event.ActionEvent;
import javafx.geometry.Rectangle2D;
import javafx.scene.Scene;
import javafx.scene.control.Button;
import javafx.scene.control.Label;
import javafx.scene.layout.BorderPane;
import javafx.stage.Screen;
import javafx.stage.Stage;
public class TimelineEvents extends Application {
private AnimationTimer timer;
Rectangle2D primaryScreenBounds = Screen.getPrimary().getVisualBounds();
double x = primaryScreenBounds.getMaxX() - 180;
double y = primaryScreenBounds.getMaxY() - 180;
double centerX = primaryScreenBounds.getWidth() / 2;
double centerY = primaryScreenBounds.getHeight() / 2;
double tickX = (x - centerX) / primaryScreenBounds.getWidth() * 10;
double tickY = (y - centerY) / primaryScreenBounds.getHeight() * 10;
#Override
public void start(Stage stage) {
Button btn = new Button();
btn.setText("Show Message");
btn.setOnAction((ActionEvent event) -> {
Label l = new Label("Message");
BorderPane root = new BorderPane(l);
Scene s = new Scene(root, 100, 100);
Stage window = new Stage();
window.setX(x);
window.setY(y);
timer = new AnimationTimer() {
#Override
public void handle(long l) {
x = x - tickX;
y = y - tickY;
if (x >= centerX) {
window.setX(x);
window.setY(y);
} else {
stop();
}
}
};
timer.start();
window.setScene(s);
window.showAndWait();
});
BorderPane pane = new BorderPane(btn);
Scene scene = new Scene(pane, 200, 200);
stage.setScene(scene);
stage.show();
}
public static void main(String[] args) {
Application.launch(args);
}
}

Moving LinearGradient in a given Shape

Is there a way to animate a "move" of LinearGradient nested into a given Shape? Let's say for example make the LinearGradient move to right or to left? I thought about the translateXproperty or the Path object, but I want to move only the LinearGradient.
EDIT 28.09.2014
Thank you #James_D for the valueable help. But in the context of my project I've needed a different solution. I've worked it out, and the result can be seen below on the working example:
import javafx.animation.KeyFrame;
import javafx.animation.Timeline;
import javafx.application.Application;
import javafx.event.ActionEvent;
import javafx.event.EventHandler;
import javafx.scene.Parent;
import javafx.scene.Scene;
import javafx.scene.layout.BorderPane;
import javafx.scene.paint.Color;
import javafx.scene.paint.CycleMethod;
import javafx.scene.paint.LinearGradient;
import javafx.scene.paint.Stop;
import javafx.scene.shape.Rectangle;
import javafx.stage.Stage;
import javafx.util.Duration;
public class LinearGradientAnimation extends Application {
private Rectangle rect;
private Timeline timeline;
private double widthOfOneGradientCycle = 20.0;
private double gradientSlopeDegree = 45.0;
private double xStartStatic = 100.0;
private double yStartStatic = 100.0;
private double xEndStatic = xStartStatic + (widthOfOneGradientCycle * Math.cos(Math.toRadians(gradientSlopeDegree)));
private double yEndStatic = yStartStatic + (widthOfOneGradientCycle * Math.sin(Math.toRadians(gradientSlopeDegree)));
public Parent createContent() {
/* layout */
BorderPane layout = new BorderPane();
/* layout -> Rectangle */
rect = new Rectangle(0, 0, 200, 200);
/* layout -> Rectangle -> LinearGradient Animation */
timeline = new Timeline();
for (int i = 0; i < 10; i++) {
int innerIterator = i;
KeyFrame kf = new KeyFrame(Duration.millis(30 * innerIterator), new EventHandler<ActionEvent>() {
#Override
public void handle(ActionEvent ae) {
double runningRadius = innerIterator * (widthOfOneGradientCycle / 10);
double xStartDynamic = xStartStatic + (runningRadius * Math.cos(Math.toRadians(gradientSlopeDegree)));
double yStartDynamic = yStartStatic + (runningRadius * Math.sin(Math.toRadians(gradientSlopeDegree)));
double xEndDynamic = xEndStatic + (runningRadius * Math.cos(Math.toRadians(gradientSlopeDegree)));
double yEnddynamic = yEndStatic + (runningRadius * Math.sin(Math.toRadians(gradientSlopeDegree)));
LinearGradient gradient = new LinearGradient(xStartDynamic, yStartDynamic, xEndDynamic, yEnddynamic,
false, CycleMethod.REPEAT, new Stop[] {
new Stop(0.0, Color.WHITE),
new Stop(0.5, Color.BLACK),
new Stop(1.0, Color.WHITE)
});
rect.setFill(gradient);
}
});
timeline.getKeyFrames().add(kf);
}
timeline.setCycleCount(Timeline.INDEFINITE);
/* return layout */
layout.setCenter(rect);
return layout;
}
#Override
public void start(Stage stage) throws Exception {
stage.setScene(new Scene(createContent()));
stage.setWidth(300);
stage.setHeight(300);
stage.show();
timeline.play();
}
public static void main(String[] args) {
launch(args);
}
}
https://gist.github.com/bluevoxel/44dcf297ee9503e72114

Categories

Resources