Как анимировать LinearGradient на JavaFX?

1

Я пытаюсь оживить текстовое заполнение метки на JavaFX (используя JavaFX 8). Моя цель - сделать первый цвет градиента меняющимся от желтого до красного каждые полсекунды. Я пробовал это:

Timeline timeline = new Timeline();
timeline.setCycleCount(Animation.INDEFINITE);
timeline.setAutoReverse(true);

LinearGradient fill1 = new LinearGradient(50,50,200,200,false, CycleMethod.NO_CYCLE, new Stop(0.1f, Color.YELLOW), new Stop(1.0f, Color.BLACK));
LinearGradient fill2 = new LinearGradient(50,50,200,200,false, CycleMethod.NO_CYCLE, new Stop(0.1f, Color.RED), new Stop(1.0f, Color.BLACK));
KeyValue keyValue1 = new KeyValue(labelInstrucoes.textFillProperty(), fill1, Interpolator.EASE_OUT);
KeyValue keyValue2 = new KeyValue(labelInstrucoes.textFillProperty(), fill2, Interpolator.EASE_OUT);

KeyFrame keyframe1 = new KeyFrame(Duration.millis(0), keyValue1);
KeyFrame keyframe2 = new KeyFrame(Duration.millis(500), keyValue2);

timeline.getKeyFrames().addAll(keyframe1, keyframe2);

timeline.play();

Но это не сработало. Однако, если вместо LinearGradient я использую простой цвет, например:

KeyValue keyValue1 = new KeyValue(labelInstrucoes.textFillProperty(), Color.YELLOW, Interpolator.EASE_OUT);
KeyValue keyValue2 = new KeyValue(labelInstrucoes.textFillProperty(), Color.RED, Interpolator.EASE_OUT);

Оно работает. Итак, как оживить градиент?

  • 0
    AFAIK JavaFX имеет ограничения по применению интерполяторов на градиентах. Используйте onFinished () в качестве обходного пути.
Теги:
javafx
javafx-8
javafx-2

2 ответа

5

Вы можете использовать трюк css для этого, используя искаженные цвета.

Во внешнем CSS файле определите искомый цвет для начала линейного градиента и определите заполнение текста с помощью линейного градиента, который ссылается на искомый цвет:

анимированный-gradient.css:

.animated-gradient {
    -gradient-base: red ;
    -fx-text-fill: linear-gradient(to right, -gradient-base, black);
}

Затем "оживить" свойство цвета и обновить встроенный стиль, чтобы изменить значение искаженного цвета всякий раз, когда изменяется свойство:

import javafx.animation.Animation;
import javafx.animation.KeyFrame;
import javafx.animation.KeyValue;
import javafx.animation.Timeline;
import javafx.application.Application;
import javafx.beans.property.ObjectProperty;
import javafx.beans.property.SimpleObjectProperty;
import javafx.scene.Scene;
import javafx.scene.control.Label;
import javafx.scene.layout.StackPane;
import javafx.scene.paint.Color;
import javafx.stage.Stage;
import javafx.util.Duration;



public class AnimatedGradient extends Application {
    @Override
    public void start(Stage primaryStage) {
        StackPane root = new StackPane();
        Label label = new Label("Animated gradient");
        root.getChildren().add(label);
        label.getStyleClass().add("animated-gradient");
        Scene scene = new Scene(root,400,400);
        scene.getStylesheets().add(getClass().getResource("animated-gradient.css").toExternalForm());
        primaryStage.setScene(scene);
        primaryStage.show();

        ObjectProperty<Color> baseColor = new SimpleObjectProperty<>();

        KeyValue keyValue1 = new KeyValue(baseColor, Color.RED);
        KeyValue keyValue2 = new KeyValue(baseColor, Color.YELLOW);
        KeyFrame keyFrame1 = new KeyFrame(Duration.ZERO, keyValue1);
        KeyFrame keyFrame2 = new KeyFrame(Duration.millis(500), keyValue2);
        Timeline timeline = new Timeline(keyFrame1, keyFrame2);

        baseColor.addListener((obs, oldColor, newColor) -> {
            label.setStyle(String.format("-gradient-base: #%02x%02x%02x; ", 
                    (int)(newColor.getRed()*255),
                    (int)(newColor.getGreen()*255),
                    (int)(newColor.getBlue()*255)));
        });

        timeline.setAutoReverse(true);
        timeline.setCycleCount(Animation.INDEFINITE);
        timeline.play();

    }

    public static void main(String[] args) {
        launch(args);
    }
}

(Вы можете использовать Bindings.format(...) вместо слушателя, не так много.)

  • 0
    Хитрость заключается в том, чтобы анимировать свойство, слушать его и менять тему при изменении свойства (вы можете сделать это с помощью CSS или нет). Это могучий IoC. Люби это! Спасибо!
2

Вот решение без css. Его можно использовать в анимированных элементах управления. Ниже я разместил код приложения с двумя разными элементами. Вы можете запустить его и посмотреть, как он работает.

1. Это регион с анимированным фоном

public class FilledRegion extends Region {
private final Random rand;
private ObjectProperty<Color> externalColor = new SimpleObjectProperty();
private ObjectProperty<Color> internalColor = new SimpleObjectProperty();
private Color oldExternalColor;
private Color oldInternalColor;
private Background bg;
private Timeline timeline;
private int duration;

public FilledRegion() {
    rand = new Random();
    this.setMinWidth(75);
    oldExternalColor = getRandomColor(AnimatedGradients.baseExternalColor, AnimatedGradients.externalDelta);
    oldInternalColor = getRandomColor(AnimatedGradients.baseInternalColor, AnimatedGradients.internalDelta);
    externalColor.set( oldExternalColor );
    internalColor.set( oldInternalColor );
    setBackground();
    internalColor.addListener((obs, oldColor, newColor) -> {
        setBackground();
    });
}

public void startAnimation() {
    timeline = new Timeline();
    createTimelineContent();
    timeline.setOnFinished(ActionEvent -> {
        createTimelineContent();
        timeline.play();
    });
    timeline.play();
}

private void createTimelineContent() {
    timeline.getKeyFrames().clear();
    duration = getRandomDuration();
    KeyFrame kf1 =
        new KeyFrame(Duration.ZERO,
            new KeyValue( externalColor, oldExternalColor ),
            new KeyValue( internalColor, oldInternalColor ));
    oldExternalColor = getRandomColor(AnimatedGradients.baseExternalColor, AnimatedGradients.externalDelta);
    oldInternalColor = getRandomColor(AnimatedGradients.baseInternalColor, AnimatedGradients.internalDelta);
    KeyFrame kf2 =
        new KeyFrame(new Duration(duration),
            new KeyValue( externalColor, oldExternalColor ),
            new KeyValue( internalColor, oldInternalColor ));
    timeline.getKeyFrames().addAll( kf1, kf2 );
}

private void setBackground() {
    bg = new Background(
        new BackgroundFill(
            new LinearGradient(0, 0, 0, 1, true, CycleMethod.NO_CYCLE,
                new Stop[] { new Stop(0, externalColor.get()), new Stop(0.5, internalColor.get()), new Stop(1, externalColor.get())}
            ),
            new CornerRadii(0),
            new Insets(0, 0, 0, 0)
        )
    );
    this.setBackground(bg);
}

private Color getRandomColor(Color color, double delta) {
    int index = (int)( (color.getRed()+getRandomCoefficient(delta))*255 );
    int r = ( index > 255 ) ? 255 : ( (index < 0) ? 0 : index );
    index = (int)( (color.getGreen()+getRandomCoefficient(delta))*255 );
    int g = ( index > 255 ) ? 255 : ( (index < 0) ? 0 : index );
    index = (int)( (color.getBlue()+getRandomCoefficient(delta))*255 );
    int b = ( index > 255 ) ? 255 : ( (index < 0) ? 0 : index );
    return Color.rgb(r, g, b);
}

private double getRandomCoefficient(double delta) {
    return ( rand.nextDouble()*2 - 1 ) * delta;
}

private int getRandomDuration() {
    return (int)(( rand.nextDouble()*2 - 1 ) * AnimatedGradients.durationDelta * AnimatedGradients.baseDuration) + AnimatedGradients.baseDuration;
}}

2. Это пример анимированной иконки

public class AnimatedIcon extends WritableImage {
private ObjectProperty<Color> topColor = new SimpleObjectProperty();
private ObjectProperty<Color> bottomColor = new SimpleObjectProperty();
private Color oldTopColor;
private Color oldBottomColor;
private Timeline timeline;
private static final int DURATION = 5000;
private static final Random rand = new Random();

private static final int ICON_WIDTH = 32;
private static final int ICON_HEIGHT = 32;

private Stage primaryStage;

public AnimatedIcon(Stage primaryStage) {
    super(ICON_WIDTH, ICON_HEIGHT);
    this.primaryStage = primaryStage;
    oldTopColor = Color.rgb(0, 45, 0);
    oldBottomColor = Color.rgb(12, 128, 12);
    topColor.set(oldTopColor);
    bottomColor.set(oldBottomColor);
    createGraphics();
    bottomColor.addListener((obs, oldColor, newColor) -> {
        createGraphics();
    });
}

private void createGraphics() {
    PixelWriter pixelWriter = this.getPixelWriter();
    for (int y = 0; y < ICON_HEIGHT; y++) {
        for (int x = 0; x < ICON_WIDTH; x++) {
            double position = (double)x/(double)ICON_WIDTH;
            int r = (int)( ( topColor.get().getRed() - (topColor.get().getRed() - bottomColor.get().getRed())*position ) * 255 );
            int g = (int)( ( topColor.get().getGreen() - (topColor.get().getGreen() - bottomColor.get().getGreen())*position ) * 255 );
            int b = (int)( ( topColor.get().getBlue() - (topColor.get().getBlue() - bottomColor.get().getBlue())*position ) * 255 );
            double o = topColor.get().getOpacity() - (topColor.get().getOpacity() - bottomColor.get().getOpacity())*position;
            pixelWriter.setColor(x,y,Color.rgb(r,g,b,o));
        }
    }
    int index = primaryStage.getIcons().indexOf(this);
    if (index == 0) {
        primaryStage.getIcons().set(index, this);
    } else {
        primaryStage.getIcons().add(this);            
    }
}

public void startAnimation() {
    timeline = new Timeline();
    createTimelineContent();
    timeline.setOnFinished(ActionEvent -> {
        createTimelineContent();
        timeline.play();
    });
    timeline.play();
}

private void createTimelineContent() {
    timeline.getKeyFrames().clear();
    KeyFrame kf1 =
        new KeyFrame(Duration.ZERO,
            new KeyValue( topColor, oldTopColor ),
            new KeyValue( bottomColor, oldBottomColor ));
    oldTopColor = Color.rgb(rand.nextInt(256), rand.nextInt(256), rand.nextInt(256), rand.nextDouble());
    oldBottomColor = Color.rgb(rand.nextInt(256), rand.nextInt(256), rand.nextInt(256), 1);
    KeyFrame kf2 =
        new KeyFrame(new Duration(DURATION),
            new KeyValue( topColor, oldTopColor ),
            new KeyValue( bottomColor, oldBottomColor ));
    timeline.getKeyFrames().addAll( kf1, kf2 );
}}

Это основной класс для элементов, расположенных выше

public class AnimatedGradients extends Application {
protected static int baseDuration = 10000;
protected static Color baseExternalColor = Color.rgb(0, 0, 0);
protected static Color baseInternalColor = Color.rgb(127, 127, 127);
protected static double durationDelta = 0.25;
protected static double externalDelta = 0.1;
protected static double internalDelta = 1;

public static void main(String[] args) {
    launch(args);
}

@Override
public void start(Stage primaryStage) {
    HBox root = new HBox();
    root.setSpacing(1);
    root.setAlignment(Pos.CENTER);
    Scene scene = new Scene(root, 761, 500, Color.BLACK);        
    primaryStage.setScene(scene);
    AnimatedIcon ai = new AnimatedIcon(primaryStage);
    primaryStage.getIcons().add(ai);
    ai.startAnimation();
    primaryStage.setTitle("Animated Gradients");
    for (int i = 0; i < 10; i++) {
        FilledRegion gr = new FilledRegion();
        root.getChildren().add(gr);
        gr.startAnimation();
    }
    primaryStage.show();

}}

Ещё вопросы

Сообщество Overcoder
Наверх
Меню