animationuser-interfacejavafx-2javafx-8

How to make an animation with CSS in JavaFX?


I want to change the style of Node by changing its style class.

Button button = new Button();
button.getStyleClass().add("class1")   
button.setOnMouseClicked(new EventHandler<MouseEvent>() {
        @Override
        public void handle(MouseEvent mouseEvent) {
            button.getStyleClass().add("class2");
        }
    });

Is it possible to change the style gradually, for making something like a transition?


Solution

  • Is it possible to change style gradually, for example make some transition?

    Yes.

    You will need to use setStyle rather than style classes because classes are going to be static things defined in a css. There is no direct support in JavaFX css for animation. You need to perform the animation steps in Java code to modify the css style.

    I'd only really recommend this approach when you want to use css to perform the transition because there are no corresponding Java APIs easily available.

    To handle the animation, you can use the standard javafx animation Timeline to manipulate properties which the css style property depends on.

    For example, bind your style property to string. Then vary the component you want to change (in this case the colorStringProperty) in a timeline.

    warningButton.styleProperty().bind(
        new SimpleStringProperty("-fx-base: ")
            .concat(colorStringProperty)
            .concat(";")
            .concat("-fx-font-size: 20px;")
    );
    

    Here is a sample which flashes a button using css with a gradual color transition of it's base color from gray to red when pressed.

    warninggray warningred

    import javafx.animation.*;
    import javafx.application.Application;
    import javafx.beans.property.*;
    import javafx.beans.value.*;
    import javafx.event.*;
    import javafx.scene.Scene;
    import javafx.scene.control.Button;
    import javafx.scene.image.*;
    import javafx.scene.layout.StackPane;
    import javafx.scene.paint.Color;
    import javafx.stage.Stage;
    import javafx.util.Duration;
    
    /** Shows how you can modify css styles dynamically using a timeline. */
    public class Warning extends Application {
    
        private static final String BACKGROUND = "http://bobgreiner.tripod.com/1cc2ce10.jpg"; 
    
        @Override
        public void start(Stage stage) throws Exception{
            final ObjectProperty<Color> warningColor = new SimpleObjectProperty<>(Color.GRAY);
            final StringProperty colorStringProperty = createWarningColorStringProperty(warningColor);
    
            StackPane layout = new StackPane();
            layout.getChildren().addAll(
                    new ImageView(new Image(BACKGROUND)),
                    createWarningButton(
                            warningColor, 
                            colorStringProperty
                    )
            );
            stage.setScene(new Scene(layout));
            stage.show();
        }
    
        private StringProperty createWarningColorStringProperty(final ObjectProperty<Color> warningColor) {
            final StringProperty colorStringProperty = new SimpleStringProperty();
            setColorStringFromColor(colorStringProperty, warningColor);
            warningColor.addListener(new ChangeListener<Color>() {
                @Override
                public void changed(ObservableValue<? extends Color> observableValue, Color oldColor, Color newColor) {
                    setColorStringFromColor(colorStringProperty, warningColor);
                }
            });
    
            return colorStringProperty;
        }
    
        private Button createWarningButton(final ObjectProperty<Color> warningColor, StringProperty colorStringProperty) {
            final Button warningButton = new Button("Warning! Warning!");
            warningButton.styleProperty().bind(
                    new SimpleStringProperty("-fx-base: ")
                            .concat(colorStringProperty)
                            .concat(";")
                            .concat("-fx-font-size: 20px;")
            );
    
            warningButton.setOnAction(new EventHandler<ActionEvent>() {
                @Override
                public void handle(ActionEvent actionEvent) {
                    Timeline flash = new Timeline(
                        new KeyFrame(Duration.seconds(0),    new KeyValue(warningColor, Color.GRAY, Interpolator.LINEAR)),
                        new KeyFrame(Duration.seconds(0.25), new KeyValue(warningColor, Color.GRAY, Interpolator.LINEAR)),
                        new KeyFrame(Duration.seconds(1),    new KeyValue(warningColor, Color.RED,  Interpolator.LINEAR)),
                        new KeyFrame(Duration.seconds(1.25), new KeyValue(warningColor, Color.RED,  Interpolator.LINEAR))
                    );
                    flash.setCycleCount(6);
                    flash.setAutoReverse(true);
                    flash.play();
                }
            });
    
            return warningButton;
        }
    
        private void setColorStringFromColor(StringProperty colorStringProperty, ObjectProperty<Color> color) {
            colorStringProperty.set(
                    "rgba("
                            + ((int) (color.get().getRed()   * 255)) + ","
                            + ((int) (color.get().getGreen() * 255)) + ","
                            + ((int) (color.get().getBlue()  * 255)) + ","
                            + color.get().getOpacity() +
                    ")"
            );
        }
    
        public static void main(String[] args) {
            launch(args);
        }
    }