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

JavaFX画布调整大小后如何居中图像并继续绘制?

JavaFX画布Resize后居中保留清晰图案的实现方案

我刚接触JavaFX,开发了一个简单绘图程序:通过PixelWriter向Canvas逐像素绘制,利用多轴反射像素生成画布中心的动态图案。Canvas位于BorderPane的Center区域,已实现窗口resize时画布同步调整,但希望调整后图像居中,以便在更大画布上继续绘制。

已尝试的方案

  • 捕获画布快照再写回,但图像模糊,示例代码:
    SnapshotParameters params = new SnapshotParameters();
    params.setFill(Color.WHITE);
    WritableImage image = canvas.snapshot(params, null);
    canvas.getGraphicsContext2D().drawImage(image, 50, 50);
    
  • 使用GraphicsContext平移,但仅影响后续绘制,无法移动已有图像;
  • 考虑创建超大画布并居中,但不想采用预设尺寸的方案,且需保持像素级清晰,避免缩放处理。

还尝试过高DPI快照代码,清晰度有所提升,但无法确定合适的pixelScale值:

int pixelScale = 10;
WritableImage image = new WritableImage((int)Math.rint(pixelScale * canvas.getWidth()),(int)Math.rint(pixelScale * canvas.getHeight()));
SnapshotParameters params = new SnapshotParameters();
params.setTransform(Transform.scale(pixelScale, pixelScale));
params.setFill(Color.WHITE);
canvas.snapshot(params, image);     
canvas.getGraphicsContext2D().drawImage(image, 50, 50);    

可行实现方向

1. 基于中心坐标系重新绘制图案(最优方案)

因为你的图案是以画布中心为基准生成的多轴反射效果,最直接的方式是将绘制逻辑封装为独立方法,接收画布中心坐标作为参数。每次窗口resize后,清空画布,以新画布的中心为原点重新执行绘制逻辑:

  • 优势:完全避免快照的缩放模糊问题,像素级清晰,逻辑简洁,后续绘制可直接基于新中心扩展。
  • 示例代码:
    private Canvas canvas;
    
    public void init() {
        canvas = new Canvas(800, 600);
        // 初始绘制,以当前画布中心为基准
        drawCenterPattern(canvas.getGraphicsContext2D(), 
                         (int) canvas.getWidth() / 2, 
                         (int) canvas.getHeight() / 2);
    }
    
    // 核心绘制方法:以(xCenter, yCenter)为中心生成多轴反射图案
    private void drawCenterPattern(GraphicsContext gc, int xCenter, int yCenter) {
        // 先填充背景色
        gc.setFill(Color.WHITE);
        gc.fillRect(0, 0, gc.getCanvas().getWidth(), gc.getCanvas().getHeight());
    
        PixelWriter writer = gc.getPixelWriter();
        // 你的多轴反射绘制逻辑示例(替换为实际代码)
        // 绘制中心像素
        writer.setColor(xCenter, yCenter, Color.RED);
        // 生成X轴、Y轴反射像素
        for (int offset = 1; offset < 100; offset++) {
            writer.setColor(xCenter + offset, yCenter, Color.BLUE);
            writer.setColor(xCenter - offset, yCenter, Color.BLUE);
            writer.setColor(xCenter, yCenter + offset, Color.BLUE);
            writer.setColor(xCenter, yCenter - offset, Color.BLUE);
            // 添加其他轴的反射逻辑...
        }
    }
    
    // 窗口resize时触发的方法
    public void handleResize(double newWidth, double newHeight) {
        canvas.setWidth(newWidth);
        canvas.setHeight(newHeight);
        // 以新画布的中心为基准重新绘制
        drawCenterPattern(canvas.getGraphicsContext2D(), 
                         (int) newWidth / 2, 
                         (int) newHeight / 2);
    }
    

2. 维护独立像素缓冲,resize时复制居中

如果需要保留已绘制的像素(而非重新生成),可以将所有绘制操作先写入一个独立的WritableImage缓冲,窗口resize时计算居中偏移量,将缓冲像素逐像素复制到新尺寸的画布:

  • 优势:保留历史绘制的像素,无缩放模糊,完全基于原始像素操作。
  • 示例代码:
    private WritableImage pixelBuffer;
    private Canvas canvas;
    
    public void init() {
        canvas = new Canvas(800, 600);
        // 初始化缓冲,与画布尺寸一致
        pixelBuffer = new WritableImage((int) canvas.getWidth(), (int) canvas.getHeight());
        // 初始绘制到缓冲
        drawToBuffer();
        // 同步缓冲到画布
        syncBufferToCanvas();
    }
    
    // 所有绘制操作直接写入缓冲的PixelWriter
    private void drawToBuffer() {
        PixelWriter writer = pixelBuffer.getPixelWriter();
        int centerX = (int) pixelBuffer.getWidth() / 2;
        int centerY = (int) pixelBuffer.getHeight() / 2;
        // 你的逐像素、多轴反射绘制逻辑...
        writer.setColor(centerX, centerY, Color.RED);
    }
    
    // 窗口resize时触发的方法
    public void handleResize(double newWidth, double newHeight) {
        canvas.setWidth(newWidth);
        canvas.setHeight(newHeight);
        PixelWriter canvasWriter = canvas.getGraphicsContext2D().getPixelWriter();
        PixelReader bufferReader = pixelBuffer.getPixelReader();
    
        // 填充画布背景
        canvasWriter.setColor(0, 0, (int) newWidth, (int) newHeight, Color.WHITE);
    
        // 计算居中偏移量
        double offsetX = (newWidth - pixelBuffer.getWidth()) / 2;
        double offsetY = (newHeight - pixelBuffer.getHeight()) / 2;
    
        // 将缓冲像素复制到画布的居中位置
        for (int x = 0; x < pixelBuffer.getWidth(); x++) {
            for (int y = 0; y < pixelBuffer.getHeight(); y++) {
                Color pixelColor = bufferReader.getColor(x, y);
                canvasWriter.setColor((int) (offsetX + x), (int) (offsetY + y), pixelColor);
            }
        }
    
        // 可选:如果需要后续在新画布空间绘制,可扩展缓冲尺寸并复制居中
        // WritableImage newBuffer = new WritableImage((int) newWidth, (int) newHeight);
        // PixelWriter newBufferWriter = newBuffer.getPixelWriter();
        // newBufferWriter.setColor(0, 0, (int) newWidth, (int) newHeight, Color.WHITE);
        // for (int x = 0; x < pixelBuffer.getWidth(); x++) {
        //     for (int y = 0; y < pixelBuffer.getHeight(); y++) {
        //         newBufferWriter.setColor((int)(offsetX + x), (int)(offsetY + y), bufferReader.getColor(x, y));
        //     }
        // }
        // pixelBuffer = newBuffer;
    }
    
    // 同步缓冲到画布(初始化时调用)
    private void syncBufferToCanvas() {
        PixelWriter canvasWriter = canvas.getGraphicsContext2D().getPixelWriter();
        PixelReader bufferReader = pixelBuffer.getPixelReader();
        for (int x = 0; x < pixelBuffer.getWidth(); x++) {
            for (int y = 0; y < pixelBuffer.getHeight(); y++) {
                canvasWriter.setColor(x, y, bufferReader.getColor(x, y));
            }
        }
    }
    

3. 优化快照方案(应急备选)

如果必须使用快照,可通过系统屏幕的缩放因子替代硬编码的pixelScale,解决模糊问题:

// 获取当前屏幕的缩放比例
double pixelScale = Screen.getPrimary().getOutputScaleX();
WritableImage image = new WritableImage(
    (int) Math.rint(pixelScale * canvas.getWidth()),
    (int) Math.rint(pixelScale * canvas.getHeight())
);
SnapshotParameters params = new SnapshotParameters();
params.setTransform(Transform.scale(pixelScale, pixelScale));
params.setFill(Color.WHITE);
canvas.snapshot(params, image);

// 计算居中偏移量
double offsetX = (canvas.getWidth() - image.getWidth()/pixelScale) / 2;
double offsetY = (canvas.getHeight() - image.getHeight()/pixelScale) / 2;
// 按原始尺寸绘制快照,避免缩放
canvas.getGraphicsContext2D().drawImage(image, offsetX, offsetY, 
    image.getWidth()/pixelScale, image.getHeight()/pixelScale);

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.21 05:18:34