Is it possible to do a FadeTransition to sides? - animation

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

Related

JavaFX Chart: Dynamic data causes visual axis glitches when animation is enabled

Application with visual glitch
Application without visual glitch
Whenever I have a chart that has dynamically updating data and I leave chart.setAnimated(true) as it is, the axis will visually glitch every now and again as shown on the screenshots.
Is it at all possible to prevent this while keeping animation enabled?
package application;
import javafx.application.Application;
import javafx.stage.Stage;
import javafx.scene.Scene;
import javafx.scene.layout.VBox;
import javafx.scene.control.Slider;
import javafx.scene.chart.LineChart;
import javafx.scene.chart.NumberAxis;
import javafx.scene.chart.XYChart;
import javafx.geometry.Insets;
public class SavingsCalculatorApplication extends Application {
#Override
public void start(Stage stage) {
NumberAxis xAxis = new NumberAxis();
NumberAxis yAxis = new NumberAxis();
LineChart chart = new LineChart(xAxis, yAxis);
chart.setLegendVisible(false);
chart.setCreateSymbols(false);
Slider slider = new Slider(0, 100, 10);
slider.setShowTickLabels(true);
slider.setShowTickMarks(true);
slider.setPadding(new Insets(20, 40, 0, 40));
XYChart.Series data = new XYChart.Series();
chart.getData().add(data);
slider.valueProperty().addListener(event -> {
data.getData().clear();
for(int counter = 0; counter < 100; counter++) {
data.getData().add(new XYChart.Data(counter, counter * slider.getValue()));
}
});
VBox layout = new VBox();
layout.getChildren().add(slider);
layout.getChildren().add(chart);
Scene scene = new Scene(layout);
stage.setScene(scene);
stage.show();
}
public static void main(String[] args) {
launch(SavingsCalculatorApplication.class);
}
}

JavaFX: Animation not moving

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

onKeyPressed function not working in JavaFX

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.

JavaFx, how to add menuBar and drawingPane

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.

Combine Images for overlay

I try to do something similar to this post: related question
Now I try to combine several stencils. The combined stencil looks as expected, however the inverted group is either black or white, depending on the order of the group created. The result I get:
It seems that the grouping still takes some Blend-information of the childs, or maybe I just don't get the idea of the blends. Any Idea how I can achieve an overlay as in the old question but with several stencils?
Here are the images:
package application;
import javafx.application.Application;
import javafx.geometry.Insets;
import javafx.scene.Group;
import javafx.scene.Scene;
import javafx.scene.effect.BlendMode;
import javafx.scene.image.Image;
import javafx.scene.image.ImageView;
import javafx.scene.layout.HBox;
import javafx.scene.paint.Color;
import javafx.scene.shape.Rectangle;
import javafx.stage.Stage;
public class Main extends Application {
#Override
public void start(Stage stage) {
Image original = new Image(getClass().getResourceAsStream("image.jpg"));
Image stencil1 = new Image(getClass().getResourceAsStream("stencil.jpg"));
Image stencil2 = new Image(getClass().getResourceAsStream("stencil2.jpg"));
Image stencil3 = new Image(getClass().getResourceAsStream("stencil3.jpg"));
ImageView iv = new ImageView(stencil1);
ImageView iv2 = new ImageView(stencil2);
ImageView iv3 = new ImageView(stencil3);
iv2.setBlendMode(BlendMode.ADD);
iv3.setBlendMode(BlendMode.ADD);
Group stencil = new Group();
stencil.getChildren().add(iv);
stencil.getChildren().add(iv2);
stencil.getChildren().add(iv3);
Rectangle whiteRect = new Rectangle(original.getWidth(), original.getHeight());
whiteRect.setFill(Color.WHITE);
whiteRect.setBlendMode(BlendMode.DIFFERENCE);
Group inverted = new Group(stencil, whiteRect);
// display the original, composite image and stencil.
HBox layout = new HBox(10);
layout.getChildren().addAll(new ImageView(original), inverted, stencil);
layout.setPadding(new Insets(10));
stage.setScene(new Scene(layout));
stage.show();
}
public static void main(String[] args) {
launch();
}
}
I finally figured it out myself. The trick is to create always new instances of the ImageView objects, as the blending modes are applied down the scene-graph 'till the imageview-objects.
here is the code snipped that worked for me:
package application;
import javafx.application.Application;
import javafx.geometry.Insets;
import javafx.scene.Group;
import javafx.scene.Scene;
import javafx.scene.effect.BlendMode;
import javafx.scene.image.Image;
import javafx.scene.image.ImageView;
import javafx.scene.layout.HBox;
import javafx.scene.paint.Color;
import javafx.scene.shape.Rectangle;
import javafx.stage.Stage;
public class Main extends Application {
private ImageView originalImageView;
private ImageView stencilImageView;
private Image original;
private Image stencil;
private Image stencil2;
private Rectangle whiteRect;
private Group inverted;
private Group overlaidBlack;
private Rectangle redRect;
private Group redStencil;
private Group overlaidRed;
private ImageView stencilImageView2;
private Group to;
private Group to2;
#Override
public void start(Stage stage) {
original = new Image(getClass().getResourceAsStream("image.jpg"));
stencil = new Image(getClass().getResourceAsStream("stencil.jpg"));
stencil2 = new Image(getClass().getResourceAsStream("stencil2.jpg"));
stencilImageView = new ImageView(stencil);
// first invert the stencil so that it is black on white rather than white on black.
whiteRect = new Rectangle(stencil.getWidth(), stencil.getHeight());
whiteRect.setFill(Color.WHITE);
whiteRect.setBlendMode(BlendMode.DIFFERENCE);
stencilImageView2 = new ImageView(stencil2);
stencilImageView2.setBlendMode(BlendMode.ADD);
to = new Group(stencilImageView, stencilImageView2);
ImageView tmpIv = new ImageView(stencil2);
tmpIv.setBlendMode(BlendMode.ADD);
to2 = new Group(new ImageView(stencil), tmpIv);
inverted = new Group(to, whiteRect);
originalImageView = new ImageView(original);
overlaidBlack = new Group(originalImageView, inverted);
inverted.setBlendMode(BlendMode.MULTIPLY);
overlaidBlack = new Group(originalImageView, inverted);
// create a new mask with a red tint (red on black).
redRect = new Rectangle(stencil.getWidth(), stencil.getHeight());
redRect.setFill(Color.RED);
redRect.setBlendMode(BlendMode.MULTIPLY);
redStencil = new Group(to2, redRect);
redStencil.setBlendMode(BlendMode.ADD);
overlaidRed = new Group(overlaidBlack, redStencil);
// display the original, composite image and stencil.
HBox layout = new HBox(10);
layout.getChildren().addAll(new ImageView(original), overlaidRed);
layout.setPadding(new Insets(10));
stage.setScene(new Scene(layout));
stage.show();
}
public static void main(String[] args) {
launch();
}
}

Resources