是否可以在 javafx 中制作线性渐变的百分比?

is it possible to make a percentage of a linear gradient in javafx?

考虑以下线性渐变,'A'。通常,如果您指定该画笔作为矩形的背景,它会用整个渐变填充整个区域,而不管大小。 (参见 'B')。

我们试图说 'For this particular control, only utilize the first xx% of the brush for the fill' 以便我们可以实现基于百分比的渐变填充,例如 'C'。

有很多方法可以解决这个问题。我提出了三个示例解决方案:

  1. 使用固定宽度的渐变。 <- 这是我推荐的。
  2. 使用剪辑。
  3. 使用插值器。

所有提出的解决方案都采用恒定大小的简单方法。如果您想要一个动态大小,您可以通过适当的侦听器或绑定基础矩形宽度和高度来实现它,以更新相关的剪辑或渐变设置。

使用固定宽度渐变

梯度是在固定坐标而不是比例坐标中定义的(停止仍然是比例的)。矩形本身有一个宽度,它是整个宽度的百分比,线性渐变是为整个宽度定义的。任何不适合矩形的渐变部分都不会显示,从而给出所需的结果。

LinearGradient gradient = new LinearGradient(
        0, 0, W, 0,
        false,
        CycleMethod.NO_CYCLE,
        new Stop(0, Color.LAWNGREEN),
        new Stop(.5, Color.YELLOW),
        new Stop(1, Color.ORANGERED)
);

return new Rectangle(W * visiblePortion, H, gradient);

使用剪辑

在这里,我们将剪辑应用于具有渐变的矩形,因此只有矩形的一部分可见。

Rectangle rect = new Rectangle(W, H);
rect.setStyle("-fx-fill: linear-gradient(to right, lawngreen, yellow, orangered);");

Rectangle clip = new Rectangle(W * visiblePortion, H);
rect.setClip(clip);

该示例使用 css 定义渐变,但如果您愿意,您可以使用 LinearGradient API 在 Java 代码中定义它。

使用插值器

如果您想在没有所需样式的剪辑的情况下进行比例渐变,则需要调整渐变中使用的颜色和色标以匹配所需的百分比显示(使用您为计算创建的算法)。

对于双色平滑渐变来说已经足够简单了,通用的解决方案很困难,因为具有多个停止点的渐变定义变得复杂。我这里只提供一个双色平滑渐变的解决方案

该实现使用 Color.interpolate() 函数(因为 Color 实现了 Interpolatable)来计算渐变的停止颜色。

图像看起来与基于剪辑的解决方案不同,因为基于剪辑的解决方案使用了更复杂的 3 级渐变,而此解决方案仅使用简单的两种颜色渐变。

这个实现的关键是使用插值器的梯度定义:

LinearGradient gradient = new LinearGradient(
        0, 0, 1, 0,
        true,
        CycleMethod.NO_CYCLE,
        new Stop(0, Color.GREEN),
        new Stop(1, Color.GREEN.interpolate(Color.RED, visiblePortion))
);
return new Rectangle(W * visiblePortion, H, gradient);

使用进度条

图像看起来很像进度条。如果合适,您可以调查 styling a progress bar。我不会在这里提供渐变样式进度条的解决方案。如果那是你想要的,请专门提出一个新问题。

示例应用程序

import javafx.application.Application;
import javafx.geometry.*;
import javafx.geometry.Insets;
import javafx.scene.*;
import javafx.scene.control.Label;
import javafx.scene.layout.VBox;
import javafx.scene.paint.*;
import javafx.scene.shape.Rectangle;
import javafx.stage.Stage;

import java.util.function.Function;

public class GradientDemo extends Application {
    private static final double W = 500;
    private static final double H = 10;
    private static final double STEP = 0.2;

    @Override
    public void start(Stage stage) {
        final VBox layout = new VBox(10);
        layout.setAlignment(Pos.TOP_LEFT);
        layout.setPadding(new Insets(10));

        addGradients(layout, "fixedWidthTriColorGradient", this::fixedWidthTriColorGradient);
        addGradients(layout, "clippedTriColorGradient", this::clippedTriColorGradient);
        addGradients(layout, "interpolatedTwoColorGradient", this::interpolatedTwoColorGradient);

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

    private void addGradients(
            VBox layout,
            String gradientName,
            Function<Double, Node> gradientFactory
    ) {
        Label label = new Label(gradientName);
        label.setPadding(new Insets(5, 0, 0, 0));
        layout.getChildren().addAll(
                label
        );
        for (double f = STEP; f <= 1.0; f += STEP) {
            layout.getChildren().addAll(gradientFactory.apply(f));
        }
    }

    public Rectangle fixedWidthTriColorGradient(double visiblePortion) {
        LinearGradient gradient = new LinearGradient(
                0, 0, W, 0,
                false,
                CycleMethod.NO_CYCLE,
                new Stop(0, Color.LAWNGREEN),
                new Stop(.5, Color.YELLOW),
                new Stop(1, Color.ORANGERED)
        );

        return new Rectangle(W * visiblePortion, H, gradient);
    }

    public Rectangle clippedTriColorGradient(double visiblePortion) {
        Rectangle rect = new Rectangle(W, H);
        rect.setStyle("-fx-fill: linear-gradient(to right, lawngreen, yellow, orangered);");

        Rectangle clip = new Rectangle(W * visiblePortion, H);
        rect.setClip(clip);

        return rect;
    }

    public Rectangle interpolatedTwoColorGradient(double visiblePortion) {
        LinearGradient gradient = new LinearGradient(
                0, 0, 1, 0,
                true,
                CycleMethod.NO_CYCLE,
                new Stop(0, Color.GREEN),
                new Stop(1, Color.GREEN.interpolate(Color.RED, visiblePortion))
        );

        return new Rectangle(W * visiblePortion, H, gradient);
    }

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