Why is there a big performance difference between transition on Alert and Stage?
<p>I'm trying to implement some animation in my project. When the user is using the application, sometimes he or she gets Yes/No dialogs (<code>Alert</code>) for confirmation or dialog boxes (<code>Stage</code>) to input data (and press a save button). After the event, normally I would show another <code>Alert</code> with "Success" (if succesful ofcourse). </p>

<p>Now, to eliminate a bunch of extra "useless" windows/screens/popups, I wanted to minimize the <code>Alert</code> or <code>Stage</code> to the lower left corner of the screen where there will appear a "Success" message for about 3 seconds in a status bar. I have implemented this succesfully but I noticed a huge performance difference between the animation on <code>Alert</code> and the animation on <code>Stage</code>.</p>

<p><code>Alert</code> seems to animate very smoothly, while the <code>Stage</code> is actually very choppy (even on a good pc). I have read about caching and searched related questions, but without much effect or solutions. I tried to make JavaFX (Maven) example (based on some other examples I found) which you can find below. </p>

<p>You will see, when you press the <em>Show alert</em> button, and press <em>Yes</em> in the <code>Alert</code> window, the <code>Alert</code> will go smoothly to the lower left corner of the screen.
When you press the <em>Show node</em> button, and press the <em>Close</em> button in the newly opened stage, the animation goes much more choppy compared to <code>Alert</code>.</p>

<p>Is there anything I can do to smoothen the animation of the stage? I also tried to put the top AnchorPane invisible to see if there was any performance improvement, but it was exactly the same.</p>

<p><strong>Scene.fxml:</strong></p>

<pre><code>&lt;?xml version="1.0" encoding="UTF-8"?&gt;

&lt;?import java.lang.*?&gt;
&lt;?import java.util.*?&gt;
&lt;?import javafx.scene.*?&gt;
&lt;?import javafx.scene.control.*?&gt;
&lt;?import javafx.scene.layout.*?&gt;

&lt;AnchorPane id="AnchorPane" prefHeight="200" prefWidth="320" xmlns:fx="http://javafx.com/fxml/1" xmlns="http://javafx.com/javafx/8.0.40" fx:controller="proj.mavenproject1.FXMLController"&gt;
    &lt;children&gt;
    &lt;Button fx:id="button" layoutX="52.0" layoutY="90.0" onAction="#handleButtonAction" text="Show Alert" /&gt;
    &lt;Label fx:id="label" layoutX="126" layoutY="120" minHeight="16" minWidth="69" /&gt;
      &lt;Button fx:id="button1" layoutX="217.0" layoutY="90.0" onAction="#handleButtonAction2" text="Show Node" /&gt;
    &lt;/children&gt;
&lt;/AnchorPane&gt;
</code></pre>

<p><strong>testNode.fxml:</strong></p>

<pre><code>&lt;?xml version="1.0" encoding="UTF-8"?&gt;

&lt;?import java.lang.*?&gt;
&lt;?import java.util.*?&gt;
&lt;?import javafx.scene.*?&gt;
&lt;?import javafx.scene.control.*?&gt;
&lt;?import javafx.scene.layout.*?&gt;


&lt;AnchorPane id="AnchorPane" prefHeight="400.0" prefWidth="600.0" xmlns:fx="http://javafx.com/fxml/1" xmlns="http://javafx.com/javafx/8.0.40" fx:controller="proj.mavenproject1.TestNodeController"&gt;
   &lt;children&gt;
      &lt;Button layoutX="262.0" layoutY="188.0" mnemonicParsing="false" onAction="#handleButtonAction" text="Close node" /&gt;
   &lt;/children&gt;
&lt;/AnchorPane&gt;
</code></pre>

<p><strong>FXMLController.java:</strong></p>

<pre><code>package proj.mavenproject1;

import java.io.IOException;
import java.net.URL;
import java.util.ResourceBundle;
import java.util.logging.Level;
import java.util.logging.Logger;
import javafx.event.ActionEvent;
import javafx.fxml.FXML;
import javafx.fxml.Initializable;
import javafx.scene.control.Label;

public class FXMLController implements Initializable {

    @FXML
    private Label label;

    @FXML
    private void handleButtonAction(ActionEvent event) {
    Utilities.showYesNo("test", "this to test the closing animation of an alert", true);        

    System.out.println("You clicked me!");
    label.setText("Hello World!");
    }

    @FXML
    private void handleButtonAction2(ActionEvent event) {
    try {
        URL url = getClass().getResource("/fxml/testNode.fxml");
        Utilities.showDialog(url);
    } catch (IOException ex) {
        Logger.getLogger(FXMLController.class.getName()).log(Level.SEVERE, null, ex);
    }
    }

    @Override
    public void initialize(URL url, ResourceBundle rb) {
    // TODO
    }
}
</code></pre>

<p><strong>TestNodeController.java:</strong></p>

<pre><code>package proj.mavenproject1;

import java.net.URL;
import java.util.ResourceBundle;
import javafx.event.ActionEvent;
import javafx.fxml.FXML;
import javafx.fxml.Initializable;

public class TestNodeController implements Initializable {

    @FXML
    private void handleButtonAction(ActionEvent event) {
    Utilities.closeStage(event, true);
    }

    /**
     * Initializes the controller class.
     */
    @Override
    public void initialize(URL url, ResourceBundle rb) {
    // TODO
    }    

}
</code></pre>

<p><strong>Utilities.java:</strong></p>

<pre><code>package proj.mavenproject1;

import java.io.IOException;
import java.net.URL;
import java.util.Optional;
import java.util.ResourceBundle;
import javafx.animation.KeyFrame;
import javafx.animation.KeyValue;
import javafx.animation.Timeline;
import javafx.beans.property.DoubleProperty;
import javafx.beans.property.SimpleDoubleProperty;
import javafx.beans.value.ChangeListener;
import javafx.beans.value.ObservableValue;
import javafx.beans.value.WritableValue;
import javafx.event.ActionEvent;
import javafx.event.Event;
import javafx.event.EventHandler;
import javafx.fxml.FXMLLoader;
import javafx.geometry.Insets;
import javafx.geometry.Pos;
import javafx.scene.CacheHint;
import javafx.scene.Node;
import javafx.scene.Scene;
import javafx.scene.control.Alert;
import javafx.scene.control.ButtonType;
import javafx.scene.control.DialogEvent;
import javafx.scene.layout.AnchorPane;
import javafx.scene.layout.VBoxBuilder;
import javafx.stage.Modality;
import javafx.stage.Screen;
import javafx.stage.Stage;
import javafx.stage.StageStyle;
import javafx.stage.WindowEvent;
import javafx.util.Duration;

public class Utilities {
    public static boolean showYesNo(String title, String content, boolean animation) {
    Alert alert = new Alert(Alert.AlertType.CONFIRMATION);
    alert.setTitle(title);
    alert.setHeaderText(null);
    alert.setContentText(content);

    alert.getButtonTypes().setAll(ButtonType.YES, ButtonType.NO);

    alert.setOnCloseRequest((DialogEvent we) -&gt; {
        if (animation) {
        minimizeAlert(alert, animation);
        we.consume();
        }
    });

    Optional&lt;ButtonType&gt; result = alert.showAndWait();

    return result.get() == ButtonType.YES;
    }

    public static void showDialog(URL url) throws IOException {
    final Stage myDialog = new Stage();
    myDialog.initStyle(StageStyle.UTILITY);
    myDialog.initModality(Modality.APPLICATION_MODAL);

    Node n = (Node) FXMLLoader.load(url);

    Scene myDialogScene = new Scene(VBoxBuilder.create().children(n).alignment(Pos.CENTER).padding(new Insets(0)).build());

    myDialog.setScene(myDialogScene);

    myDialog.showAndWait();

    }

    private static void minimizeNode(Scene scene, boolean animation) {
    final int MILLIS = 750;

    //Node src = (Node) event.getSource();
    AnchorPane rootPane = (AnchorPane) scene.lookup("#rootPane");
    final Stage stage = (Stage) scene.getWindow();

    //animation = false; //TODO: check if this thing slows down the program, seems like context menu slows down because of it
    if (animation) {
        WritableValue&lt;Double&gt; writableHeight = new WritableValue&lt;Double&gt;() {
        @Override
        public Double getValue() {
            return stage.getHeight();
        }

        @Override
        public void setValue(Double value) {
            stage.setHeight(value);
        }
        };
        WritableValue&lt;Double&gt; writableWidth = new WritableValue&lt;Double&gt;() {
        @Override
        public Double getValue() {
            return stage.getWidth();
        }

        @Override
        public void setValue(Double value) {
            stage.setWidth(value);
        }
        };
        WritableValue&lt;Double&gt; writableOpacity = new WritableValue&lt;Double&gt;() {
        @Override
        public Double getValue() {
            return stage.getOpacity();
        }

        @Override
        public void setValue(Double value) {
            stage.setOpacity(value);
        }
        };

        EventHandler onFinished = new EventHandler&lt;ActionEvent&gt;() {
        public void handle(ActionEvent t) {
            stage.close();
        }
        };

        double currentX = stage.getX();
        double currentY = stage.getY();
        DoubleProperty x = new SimpleDoubleProperty(currentX);
        DoubleProperty y = new SimpleDoubleProperty(currentY);
        x.addListener(new ChangeListener&lt;Number&gt;() {
        @Override
        public void changed(ObservableValue&lt;? extends Number&gt; observable, Number oldValue, Number newValue) {
            stage.setX(newValue.doubleValue());
        }
        });
        y.addListener(new ChangeListener&lt;Number&gt;() {
        @Override
        public void changed(ObservableValue&lt;? extends Number&gt; observable, Number oldValue, Number newValue) {
            stage.setY(newValue.doubleValue());
        }
        });

        KeyFrame keyFrameMove = new KeyFrame(Duration.millis(MILLIS), onFinished, new KeyValue(x, 0d), new KeyValue(y, Screen.getPrimary().getBounds().getMaxY() - 25));
        KeyFrame keyFrameScale = new KeyFrame(Duration.millis(MILLIS), new KeyValue(writableWidth, 0d), new KeyValue(writableHeight, 0d));
        KeyFrame keyFrameOpacity = new KeyFrame(Duration.millis(MILLIS), new KeyValue(writableOpacity, 0d));
        Timeline timeline = new Timeline(keyFrameMove, keyFrameScale, keyFrameOpacity);

        if (rootPane != null) {
            rootPane.setVisible(false);
            //rootPane.getChildren().clear();
        }

        timeline.play();
    } else {
        stage.close();
    }
    }

    public static void minimizeAlert(Alert alert, boolean animation) {
    final int MILLIS = 750;

    if (animation) {
        WritableValue&lt;Double&gt; writableHeight = new WritableValue&lt;Double&gt;() {
        @Override
        public Double getValue() {
            return alert.getHeight();
        }

        @Override
        public void setValue(Double value) {
            alert.setHeight(value);
        }
        };
        WritableValue&lt;Double&gt; writableWidth = new WritableValue&lt;Double&gt;() {
        @Override
        public Double getValue() {
            return alert.getWidth();
        }

        @Override
        public void setValue(Double value) {
            alert.setWidth(value);
        }
        };

        EventHandler onFinished = new EventHandler&lt;ActionEvent&gt;() {
        public void handle(ActionEvent t) {
            alert.setOnCloseRequest(null);
            alert.close();
        }
        };

        double currentX = alert.getX();
        double currentY = alert.getY();
        DoubleProperty x = new SimpleDoubleProperty(currentX);
        DoubleProperty y = new SimpleDoubleProperty(currentY);
        x.addListener((obs, oldX, newX) -&gt; alert.setX(newX.doubleValue()));
        y.addListener((obs, oldY, newY) -&gt; alert.setY(newY.doubleValue()));

        KeyFrame keyFrameMove = new KeyFrame(Duration.millis(MILLIS), onFinished, new KeyValue(x, 0d), new KeyValue(y, Screen.getPrimary().getBounds().getMaxY() - 25));
        KeyFrame keyFrameScale = new KeyFrame(Duration.millis(MILLIS), new KeyValue(writableWidth, 0d), new KeyValue(writableHeight, 0d));
        Timeline timeline = new Timeline(keyFrameMove, keyFrameScale);

        timeline.play();
    } else {
        alert.close();
    }
    }

    public static void closeStage(Event event, boolean animation) {
    Node src = (Node) event.getSource();
    src.setCache(true);
    src.setCacheHint(CacheHint.SPEED);
    minimizeNode(src.getScene(), animation);
    }
}
</code></pre>