react-native-skia icon indicating copy to clipboard operation
react-native-skia copied to clipboard

SVG scaling/fitbox breaks on re-render

Open witaylor opened this issue 1 year ago • 1 comments

Description

I have an SVG rendered within a fitbox that shows as intended on first render, but the scaling breaks on subsequent re-renders.

I'm trying to add markers (with <Circle />) when a user taps on the canvas by keeping an array of these markers in state. When a marker is added, the marker itself displays fine but the SVG behind it breaks. If I then reload the app, both the markers and SVG display as intended.

Using with:

  • Expo 50.0.3
  • React Native 0.72.6

Version

0.1.234

Steps to reproduce

  1. load the initial canvas and markers
  2. tap on the canvas, which should add a marker
  3. the marker is added, but the SVG is broken

Snack, code example, screenshot, or link to a repository

Screen recording

https://github.com/Shopify/react-native-skia/assets/41151984/b4128d0d-13e9-43de-b7e6-9d7f2a158cd3

The component


export const RugbyPitchCanvas = () => {
  const [circles, setCircles] = useState<any[]>([
    { x: 361 / 2, y: 60, radius: 7, fillColor: "red" },
  ]);
  const [canvasLayout, setCanvasLayout] =
    useState<LayoutRectangle>(EMPTY_LAYOUT);

  const ref = useCanvasRef();
  const pitchSvg = useSVG(require("./rugby-union-pitch.svg"));

  const touchHandler = useTouchHandler({
    onStart: (touchInfo) => {
      const x = touchInfo.x;
      const y = touchInfo.y;
      setCircles((prevCircles) => [
        ...prevCircles,
        { x, y, radius: 10, fillColor: "red" },
      ]);
    },
    onActive: (touchInfo) => {
      // ongoing touch interactions
    },
    onEnd: (touchInfo) => {
      // when the touch ends
      console.log("> Canvas touch end");
    },
  });

  const src = rect(0, 0, pitchSvg?.width() ?? 0, pitchSvg?.height() ?? 0);
  const dst = rect(0, 0, canvasLayout?.width, canvasLayout?.height);

  return (
    <Canvas
      style={{ flex: 1, minHeight: dst.width * 1.032, backgroundColor: "blue" }}
      ref={ref}
      onLayout={(event) => setCanvasLayout(event.nativeEvent.layout)}
      onTouch={touchHandler}
    >
      <Group transform={fitbox("fitWidth", src, dst)}>
        <ImageSVG svg={pitchSvg} x={0} y={0} width={100} height={100} />
      </Group>

      <Group>
        {circles.map((circle, index) => (
          <Circle
            key={index}
            cx={circle.x}
            cy={circle.y}
            r={circle.radius}
            color={circle.fillColor}
          />
        ))}
      </Group>
    </Canvas>
  );
};

witaylor avatar Jan 26 '24 21:01 witaylor

it's a bit hard to say by looking at the code, could you prepare a small example where I could test everything almost immediately? with the svg file ready etc. Also I'm not sure what LayoutRectangle is and so on.

wcandillon avatar Feb 05 '24 14:02 wcandillon

Closing this but as soon as you have a small reproducible example that outlines the suspected issue, I will reopen it asap 💯

wcandillon avatar Mar 14 '24 14:03 wcandillon

Sorry I missed your first comment! I'll put together a better example shortly.

For LayoutRectangle, that's provided by React Native:

export interface LayoutRectangle {
  x: number;
  y: number;
  width: number;
  height: number;
}

witaylor avatar Mar 14 '24 15:03 witaylor