photo_view icon indicating copy to clipboard operation
photo_view copied to clipboard

Example for getting pixel location on image, on onTap

Open ElecSmurf opened this issue 1 year ago • 2 comments

Hi,

I'm using the PhotoView.customChild with a background image and a customPaint (stacked) over it. This is wrapped in a Listener to have scrolling with the mousewheel.

That is working great, love the performance and flexibility of your solution!

I'm now struggling however to get the pixel location on the image from an onTap event. I tried lots of ways, but don't seem to get the correct coords.

  • I know the pixelSize of my image
  • I can get the size the image is rendered in => context.findRenderObject()?.paintBounds.size
  • but can't get to the location with neither local or global position (which seem to always have the same values for me)

Is there any documentation or example on this that could help me?

Thanks in advance!

ElecSmurf avatar Nov 25 '23 22:11 ElecSmurf

I have figured it out, depending on the image ratio vs available screen ratio, there can be either in width or height some empty space that needs to be taken into account. This is my solution should it help someone:

onPointerHover: (PointerHoverEvent event) {
   Size? sizeOnScreen = context.findRenderObject()?.paintBounds.size;
   double widthFactor = (sizeOnScreen?.width ?? 1) / imageWidth;
   double heightFactor = (sizeOnScreen?.height ?? 1) / imageHeight;
   if (widthFactor < heightFactor) {
      double x = event.localPosition.dx / widthFactor;
      double imageRatio = imageHeight / imageWidth;
      double heightOnScreen = imageRatio * (sizeOnScreen?.width ?? 1);
      double heightTopSpace = ((sizeOnScreen?.height ?? 1) - heightOnScreen) / 2;
      double y = (event.localPosition.dy - heightTopSpace) / widthFactor;
      print('$x,$y');
   } else {
      double y = event.localPosition.dy / heightFactor;
      double imageRatio = imageWidth / imageHeight;
      double widthOnScreen = imageRatio * (sizeOnScreen?.height ?? 1);
      double widthSideSpace =
      ((sizeOnScreen?.width ?? 1) - widthOnScreen) / 2;
      double x = (event.localPosition.dx - widthSideSpace) / heightFactor;
      print('$x,$y');
   }
},

ElecSmurf avatar Nov 26 '23 00:11 ElecSmurf

After good night of sleep I found some errors and refactored to this solution. This works for all cases.

Offset? getPixelPosition(Offset mouseLocalLocation) {
   Size? screenSize = context.findRenderObject()?.paintBounds.size;
   double zoomFactor = _controller.scale ?? 1;

   if (screenSize != null && _controller.scale != null) {
      double xRelToCenScreenSpace = (screenSize.width / 2) - mouseLocalLocation.dx;
      double yRelToCenScreenSpace = (screenSize.height / 2) - mouseLocalLocation.dy;

      double x = ((imageWidth / 2) - (xRelToCenScreenSpace / zoomFactor)) - (_controller.position.dx / zoomFactor);
      double y = (imageHeight / 2) - (yRelToCenScreenSpace / zoomFactor) - (_controller.position.dy / zoomFactor);

      if (x < 0 || y < 0 || x > imageWidth || y > imageHeight) {
         return null;
      } else {
         print('Pos: ${x.toStringAsFixed(2)},${y.toStringAsFixed(2)}');
         return Offset(x, y);
      }
   }
   return null;
}

Maybe this method can be added as a static (helper) method on the controler?

ElecSmurf avatar Nov 26 '23 11:11 ElecSmurf