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 第二个颜色渲染)。下图可以给你一些快速的解释。