基于JFrame与AffineTransform的缩放及鼠标点击定位问题
问题:JFrame中缩放图像后鼠标点击位置无法映射到原图坐标
在使用JFrame显示BufferedImage时遇到问题:鼠标点击位置绑定到JFrame坐标,无法根据缩放比例转换为图像的相对位置。
绘图相关代码
public void paint(Graphics g) { super.paint(g); Graphics2D g2 = (Graphics2D) g; if (zoomer) { AffineTransform at = new AffineTransform(); double xRel = MouseInfo.getPointerInfo().getLocation().getX() - getLocationOnScreen().getX(); double yRel = MouseInfo.getPointerInfo().getLocation().getY() - getLocationOnScreen().getY(); double zoomDiv = zoomFactor / prevZoomFactor; xOffset = (zoomDiv) * (xOffset) + (1 - zoomDiv) * xRel; yOffset = (zoomDiv) * (yOffset) + (1 - zoomDiv) * yRel; at.translate(xOffset, yOffset); at.scale(zoomFactor, zoomFactor); totalZoom *=zoomFactor; prevZoomFactor = zoomFactor; g2.transform(at); zoomer = false; } if (dragger) { AffineTransform at = new AffineTransform(); at.translate(xOffset + xDiff, yOffset + yDiff); at.scale(zoomFactor, zoomFactor); g2.transform(at); if (released) { xOffset += xDiff; yOffset += yDiff; dragger = false; } } }
我尝试通过数学计算转换鼠标点击位置,但未能成功实现:
鼠标点击事件代码
@Override public void mouseClicked(MouseEvent me) { double x = MouseInfo.getPointerInfo().getLocation().getX() - getLocationOnScreen().getX(); double y = MouseInfo.getPointerInfo().getLocation().getY() - getLocationOnScreen().getY(); try { CommandFromClient clientSubmission= new CommandFromClient(CommandFromClient.GETBOARD); os.writeObject(clientSubmission); } catch (IOException e) { throw new RuntimeException(e); } System.out.println("x: " + getLocationOnScreen().getX()/zoomFactor + "y: " + getLocationOnScreen().getY()/zoomFactor); Point clicked = new Point((int)((me.getPoint().x*10-x)),(int)((me.getPoint().y*10-y))); System.out.println(1f/zoomFactor); System.out.println("CLICK DETECTED: " + clicked); for(int i = yellow.size()-1;i>=0;i--) { Rectangle bounds = new Rectangle(yellow.get(i).getX(),yellow.get(i).getY(),yellow.get(i).getImg().getWidth(),yellow.get(i).getImg().getHeight()); System.out.print("checks"); if(bounds.contains(clicked)){ System.out.print("collided"); System.out.print(yellow.get(i)); break; } } }
解决方案
直接手动计算坐标容易出错,尤其是同时存在平移和缩放变换时,正确的做法是利用AffineTransform的逆变换来完成屏幕坐标到原图坐标的映射。
步骤1:维护全局变换矩阵
修改paint方法,将当前使用的变换矩阵保存为全局变量,方便鼠标事件中直接调用:
// 新增全局变量,保存当前的绘图变换 private AffineTransform currentTransform = new AffineTransform(); public void paint(Graphics g) { super.paint(g); Graphics2D g2 = (Graphics2D) g; // 重置变换矩阵 currentTransform.setToIdentity(); if (zoomer) { double xRel = MouseInfo.getPointerInfo().getLocation().getX() - getLocationOnScreen().getX(); double yRel = MouseInfo.getPointerInfo().getLocation().getY() - getLocationOnScreen().getY(); double zoomDiv = zoomFactor / prevZoomFactor; xOffset = (zoomDiv) * xOffset + (1 - zoomDiv) * xRel; yOffset = (zoomDiv) * yOffset + (1 - zoomDiv) * yRel; totalZoom *= zoomFactor; prevZoomFactor = zoomFactor; zoomer = false; } // 应用平移变换 if (dragger) { currentTransform.translate(xOffset + xDiff, yOffset + yDiff); if (released) { xOffset += xDiff; yOffset += yDiff; dragger = false; } } else { currentTransform.translate(xOffset, yOffset); } // 应用缩放变换 currentTransform.scale(zoomFactor, zoomFactor); // 将变换应用到绘图上下文 g2.transform(currentTransform); }
步骤2:用逆变换转换鼠标坐标
修改mouseClicked方法,通过逆变换直接将屏幕点击坐标转换为原图坐标:
@Override public void mouseClicked(MouseEvent me) { try { CommandFromClient clientSubmission = new CommandFromClient(CommandFromClient.GETBOARD); os.writeObject(clientSubmission); } catch (IOException e) { throw new RuntimeException(e); } // 获取鼠标在组件内的坐标 Point screenPoint = me.getPoint(); try { // 创建当前变换的逆变换 AffineTransform inverseTransform = currentTransform.createInverse(); // 将屏幕坐标转换为原图坐标 Point2D originalPoint = inverseTransform.transform(screenPoint, null); int originalX = (int) Math.round(originalPoint.getX()); int originalY = (int) Math.round(originalPoint.getY()); System.out.println("原图坐标: x=" + originalX + ", y=" + originalY); // 使用转换后的坐标进行碰撞检测 Point clicked = new Point(originalX, originalY); for (int i = yellow.size() - 1; i >= 0; i--) { Rectangle bounds = new Rectangle(yellow.get(i).getX(), yellow.get(i).getY(), yellow.get(i).getImg().getWidth(), yellow.get(i).getImg().getHeight()); System.out.print("checks"); if (bounds.contains(clicked)) { System.out.print("collided"); System.out.print(yellow.get(i)); break; } } } catch (NoninvertibleTransformException e) { // 处理变换矩阵不可逆的情况(如缩放因子为0) e.printStackTrace(); } }
关键说明
- 全局的
currentTransform会在每次paint时更新,确保始终与当前绘图的变换一致。 - 逆变换会自动处理平移和缩放的反向计算,避免手动计算的误差。
- 必须捕获
NoninvertibleTransformException,当变换矩阵无法求逆时(比如缩放因子为0)会触发该异常。
内容的提问来源于stack exchange,提问作者user27466996
相关产品推荐
相关产品推荐

