4

I have this simple class:

Test.java:

import javafx.animation.FadeTransition;
import javafx.animation.Timeline;
import javafx.application.Application;
import javafx.scene.Scene;
import javafx.scene.control.Button;
import javafx.scene.layout.Pane;
import javafx.stage.Stage;
import javafx.util.Duration;

public class Test extends Application {
    @Override
    public void start(Stage stage) throws Exception {
        Pane pane = new Pane();
        
        Button testButton = new Button("Test");
        testButton.setStyle("-fx-background-color: green;");

        pane.getChildren().add(testButton);
        pane.setStyle("-fx-background-color: red;");

        FadeTransition transition = new FadeTransition(Duration.millis(5000), pane);
        transition.setFromValue(1.0);
        transition.setToValue(0.0);
        transition.setCycleCount(Timeline.INDEFINITE);
        transition.setAutoReverse(true);
        transition.play();

        Scene scene = new Scene(pane, 500, 500);

        stage.setMinWidth(500);
        stage.setMinHeight(500);

        stage.setTitle("Test");
        stage.setResizable(false);

        stage.setScene(scene);
        stage.show();
    }
}

It looks like this:

enter image description here

when it fades however it becomes this:

enter image description here

How do I make it so that the fade transition only affects the red background and doesn't affect the green button?

So that it looks like this:

enter image description here

parsecer
  • 4,758
  • 13
  • 71
  • 140
  • 1
    The solution is similar to [How to implement a transparent Pane with non-transparent children?](https://stackoverflow.com/questions/12717487/how-to-implement-a-transparent-pane-with-non-transparent-children), but will add a fade transition to the pane being faded. – jewelsea Jun 14 '22 at 23:46

2 Answers2

5

using stackpane

stack

You can use StackPane as root and both : Pane and Button children of stackpane . Button is not affected by transition since is no longer child of pane .

if you need different aligments for different nodes you can use static method setAligment from StackPane class , wich requires a child node and position as arguments

public class  App extends Application {
    @Override
    public void start(Stage stage) throws Exception {
        
        Pane pane = new Pane();
        
        Button testButton = new Button("Test");
        testButton.setStyle("-fx-background-color: green;");

        StackPane stackPane = new StackPane(pane,testButton);
        stackPane.setAlignment(Pos.TOP_LEFT);
        
        pane.setStyle("-fx-background-color: red;");

        FadeTransition transition = new FadeTransition(Duration.millis(5000), pane);
        transition.setFromValue(1.0);
        transition.setToValue(0.0);
        transition.setCycleCount(Timeline.INDEFINITE);
        transition.setAutoReverse(true);
        transition.play();

        Scene scene = new Scene(stackPane, 500, 500);

        stage.setMinWidth(500);
        stage.setMinHeight(500);

        stage.setTitle("Test");
        stage.setResizable(false);

        stage.setScene(scene);
        stage.show();
    }
}
Giovanni Contreras
  • 2,345
  • 1
  • 13
  • 22
  • 4
    This is the approach I would use. But note you could also animate the background color. Though it's a little complicated, since you can't animate the `background` property directly. You have to animate the color and set the background yourself every time the color changes (`Color` implements `Interpolatable`). – Slaw Jun 14 '22 at 23:46
  • 2
    animating color is more accurate and does not need an extra node – Giovanni Contreras Jun 14 '22 at 23:52
  • 2
    These two related [examples](https://stackoverflow.com/a/72588241/230513) contrast updating a node or the background; an example cited there implements `extends Interpolator` to animate the color.. – trashgod Jun 15 '22 at 00:11
3

This uses the alternative approach suggested by Slaw in the comments:

you could also animate the background color. Though it's a little complicated, since you can't animate the background property directly. You have to animate the color and set the background yourself every time the color changes (Color implements Interpolatable).

I also would prefer Giovanni's solution over this. This solution is mainly offered as an example of how you might create a custom transition.

There are two solutions provided.

The key to these solutions is the same in both cases, extend Transition and override the interpolate method to set the required background fill for a given interpolation value.

  1. Interpolates between two colors (the start color and a transparent color).

    protected void interpolate(double frac) {
        Color cur = from.interpolate(to, frac);
        target.setBackground(BackgroundUtil.createBackground(cur));
    }
    

    OR

  2. Interpolates just the opacity portion of a single color.

    protected void interpolate(double frac) {
        Color cur = Color.color(
                color.getRed(),
                color.getGreen(),
                color.getBlue(),
                color.getOpacity() * frac
        );
        target.setBackground(BackgroundUtil.createBackground(cur));
    }
    

Executable Example

The output of the example is similar to the screenshot in Giovanni's solution.

import javafx.animation.Transition;
import javafx.application.Application;
import javafx.scene.Scene;
import javafx.scene.control.Button;
import javafx.scene.layout.*;
import javafx.scene.paint.Color;
import javafx.stage.Stage;
import javafx.util.Duration;

public class BackgroundFade extends Application {
    @Override
    public void start(Stage stage) throws Exception {
        Button testButton = new Button("Test");
        testButton.setStyle("-fx-background-color: green;");

        Pane pane = new Pane(testButton);
        pane.setPrefSize(500, 500);
        pane.setBackground(
                BackgroundUtil.createBackground(Color.RED)
        );

        //applyBackgroundColorTransition(pane);
        applyBackgroundOpacityTransition(pane);

        stage.setScene(new Scene(pane));
        stage.show();
    }

    private void applyBackgroundColorTransition(Pane pane) {
        BackgroundColorTransition backgroundColorTransition = new BackgroundColorTransition(
                Duration.seconds(5),
                pane,
                Color.RED,
                Color.TRANSPARENT
        );
        backgroundColorTransition.setCycleCount(Transition.INDEFINITE);
        backgroundColorTransition.setAutoReverse(true);
        backgroundColorTransition.play();
    }

    private void applyBackgroundOpacityTransition(Pane pane) {
        BackgroundOpacityTransition backgroundOpacityTransition = new BackgroundOpacityTransition(
                Duration.seconds(5),
                pane,
                Color.RED
        );
        backgroundOpacityTransition.setCycleCount(Transition.INDEFINITE);
        backgroundOpacityTransition.setAutoReverse(true);
        backgroundOpacityTransition.play();
    }
}

class BackgroundColorTransition extends Transition {
    private final Region target;
    private final Color from;
    private final Color to;

    public BackgroundColorTransition(Duration duration, Region target, Color from, Color to) {
        setCycleDuration(duration);
        this.target = target;
        this.from = from;
        this.to = to;
    }

    @Override
    protected void interpolate(double frac) {
        Color cur = from.interpolate(to, frac);
        target.setBackground(BackgroundUtil.createBackground(cur));
    }
}

class BackgroundOpacityTransition extends Transition {
    private final Region target;
    private final Color color;

    public BackgroundOpacityTransition(Duration duration, Region target, Color color) {
        setCycleDuration(duration);
        this.target = target;
        this.color = color;
    }

    @Override
    protected void interpolate(double frac) {
        Color cur = Color.color(
                color.getRed(),
                color.getGreen(),
                color.getBlue(),
                color.getOpacity() * frac
        );
        target.setBackground(BackgroundUtil.createBackground(cur));
    }
}

class BackgroundUtil {
    public static Background createBackground(Color color) {
        return new Background(
                new BackgroundFill(
                        color, null, null
                )
        );
    }
}
jewelsea
  • 150,031
  • 14
  • 366
  • 406