zakodium-oss / react-roi

https://react-roi.pages.dev/
https://react-roi.pages.dev/
0 stars 0 forks source link

Controlled PanZoom component #119

Open stropitek opened 7 months ago

stropitek commented 7 months ago

To do this we need:

Ref: https://github.com/zakodium-oss/pixelium/issues/80

stropitek commented 7 months ago

We probably should have another simpler component that only handles the pan/zoom part and is controlled.

stropitek commented 7 months ago

[!NOTE]
This is a draft

We should reuse as much as possible the existing code implemented for the other components, and if needed refactor it to make it more reusable across components.

I understand that it might not be easy to navigate the existing code. Please feel completely free to ask questions while implementing this.

The current code has to following model for computing transformations. I came up with the terminology now so I haven't really used it in the code. But hope it can be hopeful to have a good mental model:

p_viewport: position in viewport (pixels on user's screen) p_reference: reference position ("normalized" with respect to the resize strategy) p_image: image position (pixels in the target image) f_resize: transform applied by resize strategy f_panzoom: trtansform applied by the user (transform prop below)

p_viewport = f_panzoom(p_reference); p_reference = f_resize(p_image);

API I'm proposing:

const [transform, setTransform] = useState({scale: 1, translation: [0, 0]});
<PanZoom
  // The PanZoom component is basically a div around the target image which style 
  // can be customized to fit into any particular layout outside of it.
  style={{}}
  className=""
  transform={transform}
  resizeStrategy="contain"
  onDoubleClick={(event, helpers) => {
    // Reset zoom level
    setTransform({scale: 1, translation: [0, 0]});
    // idea of cool feature:
    // helpers.getPosition(event, 'image');
    // With image segmentation, zoom on an object automatically
  }}
  onPanMove={(event, helpers) => {
    // Called when moving the mouse during a click -> move -> release action
    setTransform(transform => helpers.applyMovement(event, transform));
  }}
  onMouseWheel={(event, helpers) => {
    // If you need it you can get data about where the event happened
    const {x, y} = helpers.getPosition(event, 'image');
    // User controls when it should `event.preventDefault` to disable regular scrolling
    // And if zoom should happen only when `alt` key is pressed for example
    const zoomFactor = event.deltaY > 0 ? 1.2 ? 1 / 1.2;
    // Get the transform needed 
    setTransform(transform => helpers.applyZoomTowardsCenter(zoomFactor, transform));
  }}
>
  <TargetImage src="" />
</PanZoom>

It's could maybe be useful if helper methods can return coordinate information in different coordinate systems as defined above: viewport, reference, image. Like the example above onDoubleClick.

Other feature to consider later and questions: