2 Replies Latest reply: Jan 22, 2013 10:47 AM by James_D RSS

    Popup animation. Is that the right way to do it?

    csh
      Hi,

      I want to make a "smooth" transition to a popup, when it is shown. It should scale, while beeing centered.

      Here is what I came up with. Is this the best way to do things like this? I had to use a abstract Transition where I position the popup at the center, according to the current animation progress. I found it to be rather complicated, compared to other animation stuff, so I ask...

      I saw something similar being done at the Devoxx, where some Oracle JavaFX developers presented their Kiosk. They said their code is available. Does anybody know where?

      import javafx.animation.Interpolator;
      import javafx.animation.ParallelTransition;
      import javafx.animation.ScaleTransition;
      import javafx.animation.Transition;
      import javafx.application.Application;
      import javafx.event.ActionEvent;
      import javafx.event.EventHandler;
      import javafx.geometry.Insets;
      import javafx.scene.Scene;
      import javafx.scene.control.Button;
      import javafx.scene.control.Label;
      import javafx.scene.layout.StackPane;
      import javafx.scene.layout.VBox;
      import javafx.stage.Popup;
      import javafx.stage.Stage;
      import javafx.stage.WindowEvent;
      import javafx.util.Duration;
      
      public class TestApp3 extends Application {
      
          public static void main(String[] args) {
              Application.launch(args);
          }
      
          public void start(final Stage stage) throws Exception {
      
              VBox root = new VBox();
      
      
              Button button = new Button("Click");
              button.setOnAction(new EventHandler<ActionEvent>() {
                  @Override
                  public void handle(ActionEvent actionEvent) {
      
                      final Popup popup = new Popup();
                      popup.setAutoHide(true);
      
                      final VBox vBox = new VBox();
      
                      Label label = new Label("Test");
                      label.setStyle("-fx-font-size: 3em");
                      vBox.getChildren().add(label);
                      vBox.setPadding(new Insets(20, 20, 20, 20));
                      vBox.setStyle("-fx-background-color: linear-gradient(#4c9dcf, #0074bc), linear-gradient(derive(#4c9dcf, -50%), derive(#0074bc, 50%)), linear-gradient(#e5eaef, #99adc2);\n" +
                              "    -fx-background-insets: 0, 1, 2;\n" +
                              "    -fx-background-radius: 5 5 5 5;\n" +
                              "    -fx-effect: dropshadow(three-pass-box, rgba(0, 0, 0, 0.6), 8, 0.0, 0, 0);");
      
                      vBox.setOpacity(0.6);
                      final StackPane stackPane = new StackPane();
                      //stackPane.setAlignment(Pos.CENTER);
                      stackPane.getChildren().add(vBox);
      
                      popup.setOnShown(new EventHandler<WindowEvent>() {
                          @Override
                          public void handle(WindowEvent windowEvent) {
                              ScaleTransition scaleTransition = new ScaleTransition(Duration.seconds(0.2), stackPane);
                              scaleTransition.setInterpolator(Interpolator.EASE_BOTH);
                              scaleTransition.setToX(1);
                              scaleTransition.setToY(1);
                              scaleTransition.setFromX(0);
                              scaleTransition.setFromY(0);
      
      
                              final double startX = popup.getX() + popup.getWidth() / 2;
                              final double startY = popup.getY() + popup.getHeight() / 2;
                              final double endX = popup.getX();
                              final double endY = popup.getY();
      
      
                              Transition transition = new Transition() {
                                  {
                                      setCycleDuration(Duration.seconds(0.2));
                                  }
      
                                  @Override
                                  protected void interpolate(double v) {
      
                                      popup.setX(startX + (endX - startX) * v);
                                      popup.setY(startY + (endY - startY) * v);
                                  }
                              };
      
                              transition.setInterpolator(Interpolator.EASE_BOTH);
                              transition.play();
                              final ParallelTransition parallelTransition = new ParallelTransition();
      
                              parallelTransition.getChildren().addAll(transition, scaleTransition);
                              parallelTransition.playFromStart();
                          }
                      });
      
                      popup.getContent().add(stackPane);
                      popup.show(stage);
      
                  }
              });
      
              root.getChildren().add(button);
      
              Scene scene = new Scene(root);
              stage.setScene(scene);
              stage.show();
          }
      }
        • 1. Re: Popup animation. Is that the right way to do it?
          James_D
          Another way would be to add change listeners to your stack pane's scaleX and scaleY properties, and call popup.setX(...) and popup.setY(...) in the changed(...) methods. For example:
          stackPane.scaleXProperty().addListener(new ChangeListener<Number>() {
            @Override
            public void changed(
                ObservableValue<? extends Number> observable,
                Number oldValue, Number newValue) {
              popup.setX(startX + (endX - startX) * newValue.doubleValue());
            }
          });
          Then you only need the scale transition.

          This is effectively creating bindings, but you can't explicitly bind the x and y properties of the popup as they are read only.

          This is arguably no less complicated, but it does at least mean you don't have to replicate setting the Duration and interpolator for two different transitions to keep things in sync.
          • 2. Re: Popup animation. Is that the right way to do it?
            James_D
            Actually, cleaner still is to just maintain the center of the popup by listening to it's own width and height properties:
            final double popupCenterX = popup.getX() + popup.getWidth() / 2;
            final double popupCenterY = popup.getY() + popup.getHeight() / 2;
            
            popup.widthProperty().addListener(new ChangeListener<Number>() {
              @Override
              public void changed(
                  ObservableValue<? extends Number> observable,
                  Number oldValue, Number newValue) {
                popup.setX(popupCenterX - popup.getWidth() / 2 ) ;
              }
            });
            
            popup.heightProperty().addListener(new ChangeListener<Number>() {
              @Override
              public void changed(
                  ObservableValue<? extends Number> observable,
                  Number oldValue, Number newValue) {
                popup.setY(popupCenterY - popup.getHeight() / 2 ) ;
              }
            });