是否可以在 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'。
有很多方法可以解决这个问题。我提出了三个示例解决方案:
- 使用固定宽度的渐变。 <- 这是我推荐的。
- 使用剪辑。
- 使用插值器。
所有提出的解决方案都采用恒定大小的简单方法。如果您想要一个动态大小,您可以通过适当的侦听器或绑定基础矩形宽度和高度来实现它,以更新相关的剪辑或渐变设置。
使用固定宽度渐变
梯度是在固定坐标而不是比例坐标中定义的(停止仍然是比例的)。矩形本身有一个宽度,它是整个宽度的百分比,线性渐变是为整个宽度定义的。任何不适合矩形的渐变部分都不会显示,从而给出所需的结果。
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);
}
}
考虑以下线性渐变,'A'。通常,如果您指定该画笔作为矩形的背景,它会用整个渐变填充整个区域,而不管大小。 (参见 'B')。
我们试图说 'For this particular control, only utilize the first xx% of the brush for the fill' 以便我们可以实现基于百分比的渐变填充,例如 'C'。
有很多方法可以解决这个问题。我提出了三个示例解决方案:
- 使用固定宽度的渐变。 <- 这是我推荐的。
- 使用剪辑。
- 使用插值器。
所有提出的解决方案都采用恒定大小的简单方法。如果您想要一个动态大小,您可以通过适当的侦听器或绑定基础矩形宽度和高度来实现它,以更新相关的剪辑或渐变设置。
使用固定宽度渐变
梯度是在固定坐标而不是比例坐标中定义的(停止仍然是比例的)。矩形本身有一个宽度,它是整个宽度的百分比,线性渐变是为整个宽度定义的。任何不适合矩形的渐变部分都不会显示,从而给出所需的结果。
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);
}
}