gre / react-native-view-shot

Snapshot a React Native view and save it to an image
https://github.com/gre/react-native-view-shot-example
MIT License
2.64k stars 344 forks source link

Offscreen Issue #490

Closed montanhes closed 1 year ago

montanhes commented 1 year ago

I'm trying to capture a screen that can have a bigger height, so I'm need to put this offscreen. Also I'm using imperative CaptureRef.

I have a modal, inside this modal I have a webview that display what I need to capture as an image. I was able to archive this but the image always share missing some part of the view because of the scroll.

So I found the offscreen example and tried to use it without success.

I always get the Oops, snapshot failed [Error: Failed to capture view snapshot] when I try to generate the image using this method.

Can someone help?

Version & Platform

npm ls react-native react-native-view-shot

├─┬ @react-native-async-storage/async-storage@1.17.10
│ └── react-native@0.70.6 deduped
├─┬ @react-native-clipboard/clipboard@1.11.1
│ └── react-native@0.70.6 deduped
├─┬ @react-native-community/hooks@2.8.1
│ └── react-native@0.70.6 deduped
├─┬ @react-native-community/netinfo@9.3.9
│ └── react-native@0.70.6 deduped
├─┬ @react-navigation/drawer@6.5.0
│ ├─┬ @react-navigation/elements@1.3.6
│ │ └── react-native@0.70.6 deduped invalid: "^0.41.2 || ^0.57.0 || ^0.68.0" from node_modules/react-native-ping
│ └── react-native@0.70.6 deduped
├─┬ @react-navigation/native-stack@6.8.0
│ └── react-native@0.70.6 deduped
├─┬ @react-navigation/native@6.0.12
│ └── react-native@0.70.6 deduped
├─┬ @react-navigation/stack@6.3.1
│ └── react-native@0.70.6 deduped
├─┬ react-native-blob-util@0.16.4
│ └── react-native@0.70.6 deduped
├─┬ react-native-device-info@10.3.0
│ └── react-native@0.70.6 deduped
├─┬ react-native-encrypted-storage@4.0.3
│ └── react-native@0.70.6 deduped
├─┬ react-native-gesture-handler@2.6.1
│ └── react-native@0.70.6 deduped
├─┬ react-native-gzip@1.0.0
│ └── react-native@0.70.6 deduped
├─┬ react-native-keyboard-aware-scroll-view@0.9.5
│ ├─┬ react-native-iphone-x-helper@1.3.1
│ │ └── react-native@0.70.6 deduped invalid: "^0.41.2 || ^0.57.0 || ^0.68.0" from node_modules/react-native-ping
│ └── react-native@0.70.6 deduped
├─┬ react-native-mmkv@2.5.1
│ └── react-native@0.70.6 deduped
├─┬ react-native-ping@1.2.7
│ └── react-native@0.70.6 deduped invalid: "^0.41.2 || ^0.57.0 || ^0.68.0" from node_modules/react-native-ping
├─┬ react-native-quick-md5@3.0.4
│ └── react-native@0.70.6 deduped invalid: "^0.41.2 || ^0.57.0 || ^0.68.0" from node_modules/react-native-ping
├─┬ react-native-reanimated@2.13.0
│ └── react-native@0.70.6 deduped invalid: "^0.41.2 || ^0.57.0 || ^0.68.0" from node_modules/react-native-ping
├─┬ react-native-safe-area-context@4.3.3
│ └── react-native@0.70.6 deduped invalid: "^0.41.2 || ^0.57.0 || ^0.68.0" from node_modules/react-native-ping
├─┬ react-native-screens@3.17.0
│ └── react-native@0.70.6 deduped invalid: "^0.41.2 || ^0.57.0 || ^0.68.0" from node_modules/react-native-ping
├─┬ react-native-svg@13.2.0
│ └── react-native@0.70.6 deduped invalid: "^0.41.2 || ^0.57.0 || ^0.68.0" from node_modules/react-native-ping
├─┬ react-native-view-shot@3.7.0
│ └── react-native@0.70.6 deduped invalid: "^0.41.2 || ^0.57.0 || ^0.68.0" from node_modules/react-native-ping
├─┬ react-native-webview@11.23.1
│ └── react-native@0.70.6 deduped invalid: "^0.41.2 || ^0.57.0 || ^0.68.0" from node_modules/react-native-ping
└── react-native@0.70.6 invalid: "^0.41.2 || ^0.57.0 || ^0.68.0" from node_modules/react-native-ping

Platform: Android only

Expected behavior : allow me to share the offscreen component

Actual behavior: Oops, snapshot failed [Error: Failed to capture view snapshot]

Steps to reproduce the behavior

Function that is attached on the button on the modal:

  const shareImage = async () => {  
    captureRef(receiptRef, {
      fileName: 'ficha',
      format: "png",
      quality: 1,
      snapshotContentContainer: true
    }).then(
      (uri) => shareReceiptImage(uri, card.code),
      (error) => console.error("Oops, snapshot failed", error)
    );
  };

Modal with the view to user check it before share the image or a PDF:

      <ModalContainer closeModalState={setModalShare} modalDisplayState={modalShare}>
        <Text style={styles.space}>Escolha como deseja compartilhar:</Text>

        <View style={{display: 'flex', flexDirection: 'row', width: '100%'}}>
          <Button
            style={[styles.space, {width: '50%'}]}
            onPress={() => shareReceipt(client, card)}
            status='danger'>
            PDF
          </Button>
          <Button
            style={[styles.space, {width: '50%'}]}
            onPress={() => shareImage(client, card)}
            status='info'>
            Imagem
          </Button>
        </View>

          <ScrollView>
            <WebView
              originWhitelist={['*']}
              setBuiltInZoomControls={false}
              showsHorizontalScrollIndicatorarrow_up={false}
              source={{html: receipt ? receipt : '<h1><h1/>'}}
              style={{height: 500, marginBottom: 15}}
              scalesPageToFit={true}
            />
          </ScrollView>

          <View style={{ position: "absolute", left: 1000 }}>
            <ViewShot ref={receiptRef} options={{ format: "png", quality: 1 }}>
              <WebView
                originWhitelist={['*']}
                setBuiltInZoomControls={false}
                showsHorizontalScrollIndicatorarrow_up={false}
                source={{html: receipt ? receipt : '<h1><h1/>'}}
                style={{height: 500, marginBottom: 15}}
                scalesPageToFit={true}
              />
            </ViewShot>
          </View>
      </ModalContainer>
montanhes commented 1 year ago

I was able to solve this. I used this function to get the picture:

  const shareImage = async () => {
    if (snapshotViewRef.current !== null && snapshotViewRef.current.capture) {
      try {
        const uri = await snapshotViewRef.current.capture();
        shareReceiptImage(uri, card.code);
    } catch (error) {
        console.log('error:',error)
      }
    }
  };
<ViewShot ref={snapshotViewRef} options={{ fileName: 'ficha', format: "png", quality: 1 }} style={{position: 'absolute', left: -1700}}>
  <AutoHeightWebView
    onSizeUpdated={size => console.log(size)}
    originWhitelist={['*']}
    setBuiltInZoomControls={false}
    showsHorizontalScrollIndicatorarrow_up={false}
    source={{html: receipt}}
    style={{margin: 10}}
    scalesPageToFit={true}
  />
</ViewShot>

Also I used the react-native-autoheight-webview to set the auto height and used the offscreen approach described on the docs.