You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

JavaFX缩放包含Canvas网格的Pane时节点间出现背景白线如何解决

问题描述

我有一个Pane容器,内部排布了由Canvas组成的网格。给这些Canvas上色后,调整Pane的缩放比例时,Canvas之间会出现白色线条。

缩放前效果图
缩放前效果图

缩放后效果图
缩放后效果图

现有代码逻辑:当前场景中包含一个Pane,鼠标拖拽时,通过存储了所有Canvas对应x、y索引的HashMap查找对应位置的Canvas,将找到的Canvas填充为黑色;鼠标滚动时,Pane的缩放比例每次调整0.01。

原有完整代码:

import javafx.application.Application;
import javafx.scene.Scene;
import javafx.scene.canvas.Canvas;
import javafx.scene.layout.Pane;
import javafx.scene.paint.Color;
import javafx.stage.Stage;

import java.util.HashMap;
import java.util.Map;
import java.util.Objects;

public class Main extends Application {

    private static final double SIZE = 10;

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

    @Override
    public void start(Stage stage) {
        Pane pane = new Pane();

        Scene scene = new Scene(pane);

        pane.setOnMouseDragged(mouseEvent -> {
            int x = (int) (mouseEvent.getX() / SIZE);
            int y = (int) (mouseEvent.getY() / SIZE);

            Plot plot = new Plot(x, y);
            Canvas canvas = canvases.computeIfAbsent(plot, plot1 -> {
                Canvas newCanvas = new Canvas(SIZE, SIZE);
                pane.getChildren().add(newCanvas);
                newCanvas.setLayoutX(x * SIZE);
                newCanvas.setLayoutY(y * SIZE);
                return newCanvas;
            });

            canvas.getGraphicsContext2D().setFill(Color.BLACK);
            canvas.getGraphicsContext2D().fillRect(0, 0, 50, 50);
        });

        scene.setOnScroll(scrollEvent -> {
            double scale = pane.getScaleX() + (scrollEvent.getDeltaY() > 0 ? 0.01 : -0.01);

            pane.setScaleX(scale);
            pane.setScaleY(scale);
        });

        stage.setScene(scene);
        stage.show();
    }

    private static class Plot {
        private final int x;
        private final int y;

        private Plot(int x, int y) {
            this.x = x;
            this.y = y;
        }

        @Override
        public boolean equals(Object o) {
            if (this == o) return true;
            if (o == null || getClass() != o.getClass()) return false;
            Plot plot = (Plot) o;
            return x == plot.x &&
                    y == plot.y;
        }

        @Override
        public int hashCode() {
            return Objects.hash(x, y);
        }
    }

    private final Map<Plot, Canvas> canvases = new HashMap<>();

}
问题原因
  • JavaFX对节点执行缩放渲染时,浮点坐标计算存在精度误差,再加上默认开启的抗锯齿采样,会导致相邻Canvas的边缘出现亚像素级别的间隙,透出Pane的白色背景,就表现为你看到的白色线条。
  • 你代码中Canvas的尺寸和布局坐标都为整数,缩放后坐标转换为浮点数出现小数,相邻节点的边缘无法完全贴合,进一步放大了间隙问题。
解决方案

按下面的步骤修改代码即可彻底消除白线:

  1. 关闭Canvas的图像平滑渲染,避免抗锯齿导致的亚像素间隙
  2. 给Pane设置与Canvas填充色一致的黑色背景,就算存在极微小的间隙也不会透出白色
  3. 给Canvas的宽高各加0.5px的冗余量,完全覆盖可能的间隙区域
  4. 增加缩放倍数的范围限制,避免缩放值过大或过小导致的精度异常

修改后的完整代码:

import javafx.application.Application;
import javafx.scene.Scene;
import javafx.scene.canvas.Canvas;
import javafx.scene.canvas.GraphicsContext;
import javafx.scene.layout.Background;
import javafx.scene.layout.BackgroundFill;
import javafx.scene.layout.Pane;
import javafx.scene.paint.Color;
import javafx.stage.Stage;

import java.util.HashMap;
import java.util.Map;
import java.util.Objects;

public class Main extends Application {

    private static final double SIZE = 10;
    // 边缘冗余像素,完全覆盖间隙
    private static final double EDGE_PADDING = 0.5;
    private final Map<Plot, Canvas> canvases = new HashMap<>();

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

    @Override
    public void start(Stage stage) {
        Pane pane = new Pane();
        // Pane设置黑色背景,避免间隙透出白色
        pane.setBackground(new Background(new BackgroundFill(Color.BLACK, null, null)));
        Scene scene = new Scene(pane, Color.BLACK);

        pane.setOnMouseDragged(mouseEvent -> {
            int x = (int) (mouseEvent.getX() / SIZE);
            int y = (int) (mouseEvent.getY() / SIZE);

            Plot plot = new Plot(x, y);
            Canvas canvas = canvases.computeIfAbsent(plot, plot1 -> {
                Canvas newCanvas = new Canvas(SIZE + EDGE_PADDING, SIZE + EDGE_PADDING);
                GraphicsContext gc = newCanvas.getGraphicsContext2D();
                // 关闭图像平滑,消除抗锯齿带来的间隙
                gc.setImageSmoothing(false);
                pane.getChildren().add(newCanvas);
                newCanvas.setLayoutX(x * SIZE);
                newCanvas.setLayoutY(y * SIZE);
                return newCanvas;
            });

            canvas.getGraphicsContext2D().setFill(Color.BLACK);
            canvas.getGraphicsContext2D().fillRect(0, 0, SIZE + EDGE_PADDING, SIZE + EDGE_PADDING);
        });

        scene.setOnScroll(scrollEvent -> {
            double scale = pane.getScaleX() + (scrollEvent.getDeltaY() > 0 ? 0.01 : -0.01);
            // 限制缩放范围,避免精度异常
            scale = Math.max(0.1, Math.min(10, scale));
            pane.setScaleX(scale);
            pane.setScaleY(scale);
        });

        stage.setScene(scene);
        stage.show();
    }

    private static class Plot {
        private final int x;
        private final int y;

        private Plot(int x, int y) {
            this.x = x;
            this.y = y;
        }

        @Override
        public boolean equals(Object o) {
            if (this == o) return true;
            if (o == null || getClass() != o.getClass()) return false;
            Plot plot = (Plot) o;
            return x == plot.x && y == plot.y;
        }

        @Override
        public int hashCode() {
            return Objects.hash(x, y);
        }
    }
}

内容的提问来源于stack exchange,提问作者Zev G

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.09.24 03:24:01