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
相关产品推荐
相关产品推荐

