Shopify / react-native-skia

High-performance React Native Graphics using Skia

Home Page:https://shopify.github.io/react-native-skia

Geek Repo:Geek Repo

Github PK Tool:Github PK Tool

makeImageFromView. Android crash when attempting to create an image from a view that is outside the viewport.

chiefchief opened this issue · comments

Description

I'm implementing a signature pad feature where users can update their signature. Upon updating, I need to capture the user's first and last name and take a screenshot. However, my view is located outside the viewport. I'm using Expo for development. Interestingly, everything works fine when the app is in debug mode. However, upon building the internal distribution version, the app crashes.

Below is the crash report from Sentry.
Screenshot 2024-03-04 at 12 54 13

Version

^0.1.240

Steps to reproduce

described above

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

import { Canvas, Path, Skia, makeImageFromView, useCanvasRef, useTouchHandler } from '@shopify/react-native-skia';
import React, { FC, useCallback, useEffect, useRef, useState } from 'react';
import { Image, Text, View, useWindowDimensions } from 'react-native';

const activePath = Skia.Path.Make();

type SignatureProps = {
  width: number;
  height: number;
  data: { name: string; secondName: string };
  onUpdate?: (img: string) => void;
};

export const Signature: FC<SignatureProps> = ({ height, width, data, onUpdate }) => {
  const { width: screenWidth } = useWindowDimensions();
  const viewRef = useRef<View>(null);
  const canvasRef = useCanvasRef();
  const [imageSrc, setImageSrc] = useState('');

  useEffect(() => {
    return () => {
      activePath.reset();
    };
  }, []);

  const onTouch = useTouchHandler({
    onStart: ({ x, y }) => {
      activePath.moveTo(x, y);
    },
    onActive: ({ x, y }) => {
      activePath.lineTo(x, y);
    },
    onEnd: () => {
      const tempImage = canvasRef.current?.makeImageSnapshot();
      const resImage = tempImage?.encodeToBase64();
      if (resImage) {
        setImageSrc(`data:image/png;base64,${resImage}`);
      }
    },
  });

  const onLoad = useCallback(() => {
    // crash here
    makeImageFromView(viewRef).then((res) => {
      const base64Image = res?.encodeToBase64();
      if (base64Image) {
        onUpdate?.(base64Image);
      }
    });
  }, [onUpdate]);

  return (
    <>
      <View ref={viewRef} style={{ position: 'absolute', left: screenWidth }}>
        <View style={{ flexDirection: 'row' }}>
          <View style={{ flex: 1 }}>
            <Text>{`Name: ${data.name}`}</Text>
          </View>
          <View style={{ flex: 1, alignItems: 'flex-end' }}>
            <Text>{`Second Name: ${data.secondName}`}</Text>
          </View>
        </View>
        {imageSrc && <Image source={{ uri: imageSrc }} style={{ width, height }} onLoad={onLoad} />}
      </View>
      <Canvas ref={canvasRef} style={{ height, width, backgroundColor: 'white' }} onTouch={onTouch}>
        <Path path={activePath} style={'stroke'} strokeWidth={2} />
      </Canvas>
    </>
  );
};

I have the same issue https://shopify.github.io/react-native-skia/docs/canvas/overview
just copy and paste crash on android

I believe you need a collapsable={false} on your viewRef to solve this on android for views outside viewport.
See https://shopify.github.io/react-native-skia/docs/snapshotviews/

return (
    <>
      <View ref={viewRef} style={{ position: 'absolute', left: screenWidth }} collapsable={false}>
        <View style={{ flexDirection: 'row' }}>
          <View style={{ flex: 1 }}>
            <Text>{`Name: ${data.name}`}</Text>
          </View>
          <View style={{ flex: 1, alignItems: 'flex-end' }}>
            <Text>{`Second Name: ${data.secondName}`}</Text>
          </View>
        </View>
        {imageSrc && <Image source={{ uri: imageSrc }} style={{ width, height }} onLoad={onLoad} />}
      </View>
      <Canvas ref={canvasRef} style={{ height, width, backgroundColor: 'white' }} onTouch={onTouch}>
        <Path path={activePath} style={'stroke'} strokeWidth={2} />
      </Canvas>
    </>
  );

I believe you need a collapsable={false} on your viewRef to solve this on android for views outside viewport. See https://shopify.github.io/react-native-skia/docs/snapshotviews/

return (
    <>
      <View ref={viewRef} style={{ position: 'absolute', left: screenWidth }} collapsable={false}>
        <View style={{ flexDirection: 'row' }}>
          <View style={{ flex: 1 }}>
            <Text>{`Name: ${data.name}`}</Text>
          </View>
          <View style={{ flex: 1, alignItems: 'flex-end' }}>
            <Text>{`Second Name: ${data.secondName}`}</Text>
          </View>
        </View>
        {imageSrc && <Image source={{ uri: imageSrc }} style={{ width, height }} onLoad={onLoad} />}
      </View>
      <Canvas ref={canvasRef} style={{ height, width, backgroundColor: 'white' }} onTouch={onTouch}>
        <Path path={activePath} style={'stroke'} strokeWidth={2} />
      </Canvas>
    </>
  );

i tried but didn't help, also tried - removeClippedSubviews: false, the same result

this should be fixed now, if it's still happening let us know and we will add it to the test suite.