概要

JPanelに表示した画像にAffineTransformによる変換を適用して、マウスを使った拡大・縮小・移動を実行します。

サンプルコード

class ZoomAndPanHandler extends MouseAdapter {
  private static final double ZOOM_FACTOR = 1.2;
  private static final int MIN_ZOOM = -10;
  private static final int MAX_ZOOM = 10;
  private static final int EXTENT = 1;
  private final BoundedRangeModel zoomRange = new DefaultBoundedRangeModel(
      0, EXTENT, MIN_ZOOM, MAX_ZOOM + EXTENT);
  private final AffineTransform coordAndZoomAtf = new AffineTransform();
  private final Point2D dragStartPoint = new Point();

  @Override public void mousePressed(MouseEvent e) {
    dragStartPoint.setLocation(e.getPoint());
  }

  @Override public void mouseDragged(MouseEvent e) {
    Point2D dragEndPoint = e.getPoint();
    Point2D dragStart = transformPoint(dragStartPoint);
    Point2D dragEnd = transformPoint(dragEndPoint);
    coordAndZoomAtf.translate(
        dragEnd.getX() - dragStart.getX(), dragEnd.getY() - dragStart.getY());
    dragStartPoint.setLocation(dragEndPoint);
    e.getComponent().repaint();
  }

  @Override public void mouseWheelMoved(MouseWheelEvent e) {
    int dir = e.getWheelRotation();
    int z = zoomRange.getValue();
    zoomRange.setValue(z + EXTENT * (dir > 0 ? -1 : 1));
    if (z != zoomRange.getValue()) {
      double scale = dir > 0 ? 1d / ZOOM_FACTOR : ZOOM_FACTOR;
      Point2D pt;
      if (e.isControlDown()) {
        Rectangle r = e.getComponent().getBounds();
        pt = new Point2D.Double(r.getCenterX(), r.getCenterY());
      } else {
        pt = transformPoint(e.getPoint());
      }
      coordAndZoomAtf.translate(pt.getX(), pt.getY());
      coordAndZoomAtf.scale(scale, scale);
      coordAndZoomAtf.translate(-pt.getX(), -pt.getY());
      e.getComponent().repaint();
    }
  }

  // https://community.oracle.com/thread/1263955
  // How to implement Zoom & Pan in Java using Graphics2D
  private Point2D transformPoint(Point2D p1) {
    AffineTransform inverse = coordAndZoomAtf;
    boolean hasInverse = coordAndZoomAtf.getDeterminant() != 0d;
    if (hasInverse) {
      try {
        inverse = coordAndZoomAtf.createInverse();
      } catch (NoninvertibleTransformException ex) {
        // should never happen
        assert false;
      }
    }
    Point2D p2 = new Point();
    inverse.transform(p1, p2);
    return p2;
  }

  public AffineTransform getCoordAndZoomTransform() {
    return coordAndZoomAtf;
  }
}
View in GitHub: Java, Kotlin

解説

AffineTransform#createInverse()で取得したAffineTransformオブジェクトでマウス位置の逆変換を行い、現在表示されている倍率でのズームの中心や移動距離などを計算しています。

  • ズーム
    • ホイール上回転で拡大
    • ホイール下回転で縮小
  • スクロール
    • マウスドラッグで移動

参考リンク

コメント