JavaFX css 网格不一致
JavaFX css grid inconsistent
以下代码生成可无限平移的网格,还允许放大和缩小。正在使用 css.
绘制网格
我的问题是 css 网格在放大非常近或缩小很多时效果不佳。它也是不一致的,有时会只显示垂直线或水平线。我不确定如何可靠地重现它,它有时会发生。
完美的解决方案类似于 blender 中的网格,其中新的线条会取代不再可见的较小线条。
创建和设置的部分css:
private void reSetStyle(Pane pane) {
Scene scene = pane.getScene();
double startPointX = pane.getTranslateX()+(scene.getWidth()/2)+(0.5*scale);
double endPointX = pane.getTranslateX()+(scene.getWidth()/2)+(10.5*scale);
double zeroX = pane.getTranslateX()+(scene.getWidth()/2)*scale;
startPointX = Math.round(startPointX * 1000d)/1000d;
endPointX = Math.round(endPointX * 1000d)/1000d;
zeroX = Math.round(zeroX * 1000d)/1000d;
double startPointY = pane.getTranslateY()+(scene.getHeight()/2)+(0.5*scale);
double endPointY = pane.getTranslateY()+(scene.getHeight()/2)+(10.5*scale);
double zeroY = pane.getTranslateY()+(scene.getHeight()/2)*scale;
startPointY = Math.round(startPointY * 1000d)/1000d;
endPointY = Math.round(endPointY * 1000d)/1000d;
zeroY = Math.round(zeroY * 1000d)/1000d;
pane.getParent().setStyle("-fx-background-color: #393939," +
"linear-gradient(from "+startPointX+"px "+zeroX+"px to "+endPointX+"px "+zeroX+"px, repeat, #2f2f2f 5%, transparent 6%)," +
"linear-gradient(from "+zeroY+"px "+startPointY+"px to "+zeroY+"px "+endPointY+"px, repeat, #2f2f2f 5%, transparent 6%);");
}
完整代码:
public class Main extends Application {
private double scale = 1d, minimumScale = 0.2, maximumScale = 2.5;
@Override
public void start(Stage primaryStage) {
Pane pane = new Pane();
StackPane stackPane = new StackPane();
pane.translateXProperty().addListener((observableValue, number, t1) -> reSetStyle(pane));
pane.translateYProperty().addListener((observableValue, number, t1) -> reSetStyle(pane));
new Pannable(pane, stackPane);
stackPane.getChildren().add(pane);
stackPane.addEventFilter(ScrollEvent.ANY, scrollEvent -> {
scrollEvent.consume();
zoom(scrollEvent, pane);
});
primaryStage.setTitle("broken css");
primaryStage.setScene(new Scene(new BorderPane(stackPane), 640, 480));
primaryStage.show();
reSetStyle(pane);
}
private void zoom(ScrollEvent scrollEvent, Pane pane) {
if (scrollEvent.getDeltaY() == 0) return;
double SCALE_DELTA = 0.1;
double scaleFactor = (scrollEvent.getDeltaY() > 0) ? SCALE_DELTA : -1 * SCALE_DELTA;
double nonZoomedXOffset = pane.getTranslateX() / scale;
double nonZoomedYOffset = pane.getTranslateY() / scale;
//Rounding is needed because java will cause floating point errors otherwise
scale = clamp(minimumScale, Math.round((pane.getScaleX() + scaleFactor) * 1000d)/1000d, maximumScale);
pane.setScaleX(scale);
pane.setScaleY(scale);
pane.setTranslateX(nonZoomedXOffset * scale);
pane.setTranslateY(nonZoomedYOffset * scale);
reSetStyle(pane);
}
private void reSetStyle(Pane pane) {
Scene scene = pane.getScene();
double startPointX = pane.getTranslateX()+(scene.getWidth()/2)+(0.5*scale);
double endPointX = pane.getTranslateX()+(scene.getWidth()/2)+(10.5*scale);
double zeroX = pane.getTranslateX()+(scene.getWidth()/2)*scale;
startPointX = Math.round(startPointX * 1000d)/1000d;
endPointX = Math.round(endPointX * 1000d)/1000d;
zeroX = Math.round(zeroX * 1000d)/1000d;
double startPointY = pane.getTranslateY()+(scene.getHeight()/2)+(0.5*scale);
double endPointY = pane.getTranslateY()+(scene.getHeight()/2)+(10.5*scale);
double zeroY = pane.getTranslateY()+(scene.getHeight()/2)*scale;
startPointY = Math.round(startPointY * 1000d)/1000d;
endPointY = Math.round(endPointY * 1000d)/1000d;
zeroY = Math.round(zeroY * 1000d)/1000d;
pane.getParent().setStyle("-fx-background-color: #393939," +
"linear-gradient(from "+startPointX+"px "+zeroX+"px to "+endPointX+"px "+zeroX+"px, repeat, #2f2f2f 5%, transparent 6%)," +
"linear-gradient(from "+zeroY+"px "+startPointY+"px to "+zeroY+"px "+endPointY+"px, repeat, #2f2f2f 5%, transparent 6%);");
}
public static void main(String[] args) {
launch(args);
}
private static class Pannable implements EventHandler<MouseEvent> {
private double lastMouseX = 0, lastMouseY = 0;
private final Node eventNode;
private final Node dragNode;
public Pannable(final Node dragNode, final Node eventNode) {
this.eventNode = eventNode;
this.dragNode = dragNode;
this.eventNode.addEventHandler(MouseEvent.ANY, this);
}
@Override
public final void handle(final MouseEvent event) {
if(!event.isPrimaryButtonDown() && !event.isMiddleButtonDown()) return;
if (MouseEvent.MOUSE_PRESSED == event.getEventType()) {
if (this.eventNode.contains(event.getX(), event.getY())) {
this.lastMouseX = event.getSceneX();
this.lastMouseY = event.getSceneY();
event.consume();
}
} else if (MouseEvent.MOUSE_DRAGGED == event.getEventType()) {
((Node) event.getSource()).setCursor(Cursor.MOVE);
final double deltaX = (event.getSceneX() - this.lastMouseX);
final double deltaY = (event.getSceneY() - this.lastMouseY);
final double initialTranslateX = dragNode.getTranslateX();
final double initialTranslateY = dragNode.getTranslateY();
dragNode.setTranslateX(initialTranslateX+deltaX);
dragNode.setTranslateY(initialTranslateY+deltaY);
this.lastMouseX = event.getSceneX();
this.lastMouseY = event.getSceneY();
}
}
}
}
据我所知,与其他方法相比,您采用的方法非常简单明了,简化了逻辑。
话虽如此,出现不良行为的原因是色标依赖 % 值,该值会根据基值不断变化。我没有对此进行深入调查,但我确信这可能是原因:)。
所以 % 的替代方法是依赖“px”。 但这里有问题...JavaFX 中存在错误 CSSParser.java class,其中,应用线性 -通过 CSS 停止的基于“px”的颜色渐变无法正常工作。
要解决此问题,请使用“setBackground”API 设置背景,而不是通过“setStyle”设置。我的意思是,而不是像
这样设置
String grad = "linear-gradient(from "+xStart+"px 0px to " + xEnd + "px 0px , repeat, transparent " + (gridSize - 1) + "px, #ffffff50 1px)";
pane.getParent().setStyle("-fx-background-color:"+grad);
使用以下方法设置背景:
String grad = "linear-gradient(from "+xStart+"px 0px to " + xEnd + "px 0px , repeat, transparent " + (gridSize - 1) + "px, #ffffff50 1px)";
Background background = new Background(new BackgroundFill(LinearGradient.valueOf(grad ), CornerRadii.EMPTY, Insets.EMPTY));
((StackPane) pane.getParent()).setBackground(background);
使用“%”色标还有另一个问题。网格线的宽度将不一致。我的意思是你不能在不同的尺度上保持相同的 px(比如 1px)。您可以在演示中注意到这一点。
考虑到所有这些,我只更改了您的 resetStyle 方法的代码如下(可能需要微调),输出显示在附件 gif 中。
private void reSetStyle(Pane pane) {
Scene scene = pane.getScene();
double defaultBlockSize = 50;//px Default size of the grid at 100% scale
double gridSize = Math.ceil(defaultBlockSize * scale);
double xStart = Math.ceil(pane.getTranslateX()+scene.getWidth()/2);
double xEnd = xStart+ gridSize;
double yStart = Math.ceil(pane.getTranslateY()+scene.getHeight()/2);
double yEnd = yStart+gridSize;
double linePx = 2; // the line thickness in px
String vLines = "linear-gradient(from "+xStart+"px 0px to " + xEnd + "px 0px , repeat, transparent " + (gridSize - linePx) + "px, #ffffff50 1px)";
String hLines = "linear-gradient(from 0px "+yStart+"px to 0px " + yEnd + "px , repeat, transparent " + (gridSize - linePx) + "px , #ffffff50 1px )";
Background background = new Background(new BackgroundFill(Paint.valueOf("#393939"), CornerRadii.EMPTY, Insets.EMPTY),
new BackgroundFill(LinearGradient.valueOf(vLines), CornerRadii.EMPTY, Insets.EMPTY),
new BackgroundFill(LinearGradient.valueOf(hLines), CornerRadii.EMPTY, Insets.EMPTY));
((StackPane) pane.getParent()).setBackground(background);
}
希望您已经充分了解 JavaFX 渐变。但以防万一,如果您需要更多详细信息,可以参考此博客 JavaFX Gradients。这是我在 2012 年写的非常古老的博客。我认为没有太大变化 ;)
[更新] : 我刚刚在代码中发现了一些小问题。一旦我修复它,将再次更新答案。
[更新 2] : 看起来渐变在尝试呈现某些浮点值时出现问题。将这些值四舍五入后,问题似乎已解决。还更新了代码以将缩放点设置在中心。你能试一试吗?我更新了我的代码和 gif。
[更新 3] : 线宽说明
“gridSize”是我们为线性渐变渲染设置的数量。渐变在这个大小之后重复。所以给定两种颜色,<color1 stop1>, <color2 stop2>
<color1>
渲染到 <stop1>
量(px),然后从那里 <color2>
开始渲染直到 <stop2>
。在这种情况下,stop2 值无关紧要(它可以是 1px 或 2px 甚至 100px)。所以几乎 stop1 值将决定线条粗细(a.k.a 第二个颜色渲染)。下图可以给你一些快速的解释。
以下代码生成可无限平移的网格,还允许放大和缩小。正在使用 css.
绘制网格我的问题是 css 网格在放大非常近或缩小很多时效果不佳。它也是不一致的,有时会只显示垂直线或水平线。我不确定如何可靠地重现它,它有时会发生。
完美的解决方案类似于 blender 中的网格,其中新的线条会取代不再可见的较小线条。
创建和设置的部分css:
private void reSetStyle(Pane pane) {
Scene scene = pane.getScene();
double startPointX = pane.getTranslateX()+(scene.getWidth()/2)+(0.5*scale);
double endPointX = pane.getTranslateX()+(scene.getWidth()/2)+(10.5*scale);
double zeroX = pane.getTranslateX()+(scene.getWidth()/2)*scale;
startPointX = Math.round(startPointX * 1000d)/1000d;
endPointX = Math.round(endPointX * 1000d)/1000d;
zeroX = Math.round(zeroX * 1000d)/1000d;
double startPointY = pane.getTranslateY()+(scene.getHeight()/2)+(0.5*scale);
double endPointY = pane.getTranslateY()+(scene.getHeight()/2)+(10.5*scale);
double zeroY = pane.getTranslateY()+(scene.getHeight()/2)*scale;
startPointY = Math.round(startPointY * 1000d)/1000d;
endPointY = Math.round(endPointY * 1000d)/1000d;
zeroY = Math.round(zeroY * 1000d)/1000d;
pane.getParent().setStyle("-fx-background-color: #393939," +
"linear-gradient(from "+startPointX+"px "+zeroX+"px to "+endPointX+"px "+zeroX+"px, repeat, #2f2f2f 5%, transparent 6%)," +
"linear-gradient(from "+zeroY+"px "+startPointY+"px to "+zeroY+"px "+endPointY+"px, repeat, #2f2f2f 5%, transparent 6%);");
}
完整代码:
public class Main extends Application {
private double scale = 1d, minimumScale = 0.2, maximumScale = 2.5;
@Override
public void start(Stage primaryStage) {
Pane pane = new Pane();
StackPane stackPane = new StackPane();
pane.translateXProperty().addListener((observableValue, number, t1) -> reSetStyle(pane));
pane.translateYProperty().addListener((observableValue, number, t1) -> reSetStyle(pane));
new Pannable(pane, stackPane);
stackPane.getChildren().add(pane);
stackPane.addEventFilter(ScrollEvent.ANY, scrollEvent -> {
scrollEvent.consume();
zoom(scrollEvent, pane);
});
primaryStage.setTitle("broken css");
primaryStage.setScene(new Scene(new BorderPane(stackPane), 640, 480));
primaryStage.show();
reSetStyle(pane);
}
private void zoom(ScrollEvent scrollEvent, Pane pane) {
if (scrollEvent.getDeltaY() == 0) return;
double SCALE_DELTA = 0.1;
double scaleFactor = (scrollEvent.getDeltaY() > 0) ? SCALE_DELTA : -1 * SCALE_DELTA;
double nonZoomedXOffset = pane.getTranslateX() / scale;
double nonZoomedYOffset = pane.getTranslateY() / scale;
//Rounding is needed because java will cause floating point errors otherwise
scale = clamp(minimumScale, Math.round((pane.getScaleX() + scaleFactor) * 1000d)/1000d, maximumScale);
pane.setScaleX(scale);
pane.setScaleY(scale);
pane.setTranslateX(nonZoomedXOffset * scale);
pane.setTranslateY(nonZoomedYOffset * scale);
reSetStyle(pane);
}
private void reSetStyle(Pane pane) {
Scene scene = pane.getScene();
double startPointX = pane.getTranslateX()+(scene.getWidth()/2)+(0.5*scale);
double endPointX = pane.getTranslateX()+(scene.getWidth()/2)+(10.5*scale);
double zeroX = pane.getTranslateX()+(scene.getWidth()/2)*scale;
startPointX = Math.round(startPointX * 1000d)/1000d;
endPointX = Math.round(endPointX * 1000d)/1000d;
zeroX = Math.round(zeroX * 1000d)/1000d;
double startPointY = pane.getTranslateY()+(scene.getHeight()/2)+(0.5*scale);
double endPointY = pane.getTranslateY()+(scene.getHeight()/2)+(10.5*scale);
double zeroY = pane.getTranslateY()+(scene.getHeight()/2)*scale;
startPointY = Math.round(startPointY * 1000d)/1000d;
endPointY = Math.round(endPointY * 1000d)/1000d;
zeroY = Math.round(zeroY * 1000d)/1000d;
pane.getParent().setStyle("-fx-background-color: #393939," +
"linear-gradient(from "+startPointX+"px "+zeroX+"px to "+endPointX+"px "+zeroX+"px, repeat, #2f2f2f 5%, transparent 6%)," +
"linear-gradient(from "+zeroY+"px "+startPointY+"px to "+zeroY+"px "+endPointY+"px, repeat, #2f2f2f 5%, transparent 6%);");
}
public static void main(String[] args) {
launch(args);
}
private static class Pannable implements EventHandler<MouseEvent> {
private double lastMouseX = 0, lastMouseY = 0;
private final Node eventNode;
private final Node dragNode;
public Pannable(final Node dragNode, final Node eventNode) {
this.eventNode = eventNode;
this.dragNode = dragNode;
this.eventNode.addEventHandler(MouseEvent.ANY, this);
}
@Override
public final void handle(final MouseEvent event) {
if(!event.isPrimaryButtonDown() && !event.isMiddleButtonDown()) return;
if (MouseEvent.MOUSE_PRESSED == event.getEventType()) {
if (this.eventNode.contains(event.getX(), event.getY())) {
this.lastMouseX = event.getSceneX();
this.lastMouseY = event.getSceneY();
event.consume();
}
} else if (MouseEvent.MOUSE_DRAGGED == event.getEventType()) {
((Node) event.getSource()).setCursor(Cursor.MOVE);
final double deltaX = (event.getSceneX() - this.lastMouseX);
final double deltaY = (event.getSceneY() - this.lastMouseY);
final double initialTranslateX = dragNode.getTranslateX();
final double initialTranslateY = dragNode.getTranslateY();
dragNode.setTranslateX(initialTranslateX+deltaX);
dragNode.setTranslateY(initialTranslateY+deltaY);
this.lastMouseX = event.getSceneX();
this.lastMouseY = event.getSceneY();
}
}
}
}
据我所知,与其他方法相比,您采用的方法非常简单明了,简化了逻辑。
话虽如此,出现不良行为的原因是色标依赖 % 值,该值会根据基值不断变化。我没有对此进行深入调查,但我确信这可能是原因:)。
所以 % 的替代方法是依赖“px”。 但这里有问题...JavaFX 中存在错误 CSSParser.java class,其中,应用线性 -通过 CSS 停止的基于“px”的颜色渐变无法正常工作。
要解决此问题,请使用“setBackground”API 设置背景,而不是通过“setStyle”设置。我的意思是,而不是像
这样设置String grad = "linear-gradient(from "+xStart+"px 0px to " + xEnd + "px 0px , repeat, transparent " + (gridSize - 1) + "px, #ffffff50 1px)";
pane.getParent().setStyle("-fx-background-color:"+grad);
使用以下方法设置背景:
String grad = "linear-gradient(from "+xStart+"px 0px to " + xEnd + "px 0px , repeat, transparent " + (gridSize - 1) + "px, #ffffff50 1px)";
Background background = new Background(new BackgroundFill(LinearGradient.valueOf(grad ), CornerRadii.EMPTY, Insets.EMPTY));
((StackPane) pane.getParent()).setBackground(background);
使用“%”色标还有另一个问题。网格线的宽度将不一致。我的意思是你不能在不同的尺度上保持相同的 px(比如 1px)。您可以在演示中注意到这一点。
考虑到所有这些,我只更改了您的 resetStyle 方法的代码如下(可能需要微调),输出显示在附件 gif 中。
private void reSetStyle(Pane pane) {
Scene scene = pane.getScene();
double defaultBlockSize = 50;//px Default size of the grid at 100% scale
double gridSize = Math.ceil(defaultBlockSize * scale);
double xStart = Math.ceil(pane.getTranslateX()+scene.getWidth()/2);
double xEnd = xStart+ gridSize;
double yStart = Math.ceil(pane.getTranslateY()+scene.getHeight()/2);
double yEnd = yStart+gridSize;
double linePx = 2; // the line thickness in px
String vLines = "linear-gradient(from "+xStart+"px 0px to " + xEnd + "px 0px , repeat, transparent " + (gridSize - linePx) + "px, #ffffff50 1px)";
String hLines = "linear-gradient(from 0px "+yStart+"px to 0px " + yEnd + "px , repeat, transparent " + (gridSize - linePx) + "px , #ffffff50 1px )";
Background background = new Background(new BackgroundFill(Paint.valueOf("#393939"), CornerRadii.EMPTY, Insets.EMPTY),
new BackgroundFill(LinearGradient.valueOf(vLines), CornerRadii.EMPTY, Insets.EMPTY),
new BackgroundFill(LinearGradient.valueOf(hLines), CornerRadii.EMPTY, Insets.EMPTY));
((StackPane) pane.getParent()).setBackground(background);
}
希望您已经充分了解 JavaFX 渐变。但以防万一,如果您需要更多详细信息,可以参考此博客 JavaFX Gradients。这是我在 2012 年写的非常古老的博客。我认为没有太大变化 ;)
[更新] : 我刚刚在代码中发现了一些小问题。一旦我修复它,将再次更新答案。
[更新 2] : 看起来渐变在尝试呈现某些浮点值时出现问题。将这些值四舍五入后,问题似乎已解决。还更新了代码以将缩放点设置在中心。你能试一试吗?我更新了我的代码和 gif。
[更新 3] : 线宽说明
“gridSize”是我们为线性渐变渲染设置的数量。渐变在这个大小之后重复。所以给定两种颜色,<color1 stop1>, <color2 stop2>
<color1>
渲染到 <stop1>
量(px),然后从那里 <color2>
开始渲染直到 <stop2>
。在这种情况下,stop2 值无关紧要(它可以是 1px 或 2px 甚至 100px)。所以几乎 stop1 值将决定线条粗细(a.k.a 第二个颜色渲染)。下图可以给你一些快速的解释。