react-native-view-shot icon indicating copy to clipboard operation
react-native-view-shot copied to clipboard

blank image on android, iOS works well

Open ADLsourceCode opened this issue 3 years ago • 12 comments

Getting blank image on android, iOS works well , Below is the code :-


"react-native-view-shot": "^3.1.2",



  <View style={styles(theme).container}>
        <View collapsable={false}  style={[styles(theme).digitalView]} ref={viewRef}>
          <DigitalFlyar barberUser={barberUser} isTheme={isTheme} />
        </View>

<Button text={strings.download} onPress={downloadImage} style={styles(theme).buttonView} />


const downloadImage = async () => {
   console.warn("Download");
   try {
    // react-native-view-shot caputures component
    const format = Platform.OS === "android" ? "raw" : "png";
const result = Platform.OS === "android" ? "zip-base64" : "base64";
    captureRef(viewRef, {
      format: 'jpg',
      quality: 0.8,
      result,
    }).then(data => {
      // expected pattern 'width:height|', example: '1080:1731|'
      const resolution = /^(\d+):(\d+)\|/g.exec(data);
      const base64 = data.substr((resolution || [''])[0].length || 0);
      // console.warn("IMAGE DOWNLOAD",base64);
      onShareImage('data:image/jpeg;base64,' + base64);
    });
  } catch (error) {
    console.warn(error);
  }
  };

  const onShareImage = async (url: string) => {
    try {
      await Share.open({
        title: 'Barbr',
        url: url,
        failOnCancel: false,
        type: 'image/jpeg',
      })
        .then(res => {
          console.log(res);
          navigation.goBack();
        })
        .catch(err => {
          err && console.log('err', err);
        });
    } catch (error) {
      //@ts-ignore
      console.warn(error);
      showAlert('', error.message);
    }
  };

ADLsourceCode avatar Aug 07 '22 13:08 ADLsourceCode

we will need more information to pin point the actual problem.

what is DigitalFlyar made of? is it only text and layouts? is it using images? is it using some other native components like opengl stuff?

gre avatar Aug 11 '22 16:08 gre

@gre Below is DigitalFlyar


import { useFocusEffect } from '@react-navigation/native';
import {selectBarber} from 'appRedux/store/auth/selector';
import {Calendar, IconPosterNext, Link} from 'assets';
import Struck from 'assets/Struck';
import {Avatar, CardView, TextView} from 'atoms';
import React from 'react';
import {ImageBackground, View, Image} from 'react-native';
import {useSelector} from 'react-redux';
import {strings, withTheme, baseWebsite, ThemeType, showAlert} from 'utils';
import {getTheme} from 'utils/theme/thememode';
import styles from './styles';

interface DigitalFlyarProps {
  isTheme: Boolean;
}

const DigitalFlyar: React.FC<DigitalFlyarProps> = (props: DigitalFlyarProps) => {
  const {isTheme} = props;
  let url = baseWebsite;
  let barberUser = useSelector(selectBarber);
  let theme = isTheme ? getTheme(ThemeType.LIGHT) : getTheme(ThemeType.DARK);
  let imageLogo = isTheme ? require('../../../assets/BlackGreenLogo.png') : require('../../../assets/WhiteGreenLogo.png');


  // console.warn(barberUser?.image);

  // useFocusEffect(
  //   React.useCallback(() => {

  
  //   }, []),
  // );

  
  return (
    <View style={styles(theme).main}>
      <ImageBackground
        style={styles(theme).container}
        borderRadius={10}
        source={isTheme ? require('../../../assets/FlayerWhiteBgold.png') : require('../../../assets/FlayerBg.png')}>

<View style={styles(theme).profileContainer}>
       {barberUser?.image ? (

          // <Image uri={barberUser.image} width={70} />
          <Image 
  
  source={{

    uri: barberUser.image

  }} 
  style={{width: 70, height: 70, borderRadius: 400/ 2,shadowColor: 'white',
      shadowOffset: {width: 1, height: 1},
      shadowOpacity: 0.6,}} 
/>
            // <Avatar imageUrl={barberUser.image} size={70} style={styles(theme).profileiImage}
            // avatarStyle={{ borderWidth: 1, borderColor: 'black',  }}
            // containerStyle={{ borderColor:'black', borderWidth:0  }}
      
            // />
          
        ):<></>} 
        </View>

<TextView text={barberUser?.display_name} style={[styles(theme).activeLinkText]} />
        <TextView text={strings.is_now_available_on} style={styles(theme).avaliableText} />
        <View style={styles(theme).imageView}>
          <Image source={imageLogo} resizeMode="contain" style={styles(theme).logoImage} />

          {/* <Image
            source={isTheme ? require('../../../assets/BlackLogo.png') : require('../../../assets/WhiteLogo.png')}
            resizeMode="contain"
            style={styles(theme).logoImage}
          />
          <View style={styles(theme).greenLogoView}>
            <Image source={require('../../../assets/GreenLogo.png')} resizeMode="contain" style={styles(theme).logoImage} />
          </View> */}
        </View>
        <View style={styles(theme).bookView}>
          <TextView text={strings.book_your_next_trim} style={styles(theme).bookText} />
          <CardView style={isTheme ? styles(theme).lightlinkView : styles(theme).linkView}>
            <TextView text={`${url}`} style={[styles(theme).linkText]} />
            <TextView text={barberUser?.username} style={styles(theme).linkUserName} />
          </CardView>
        </View>
        <View style={styles(theme).footerView}>
          <View style={styles(theme).calendarContainer}>
            <Link />
            {/* <View style={styles(theme).roundView}>
              <TextView text="1" style={styles(theme).digitText} />
            </View> */}
            <TextView text={strings.tap_link.toLocaleUpperCase()} style={styles(theme).tapLinkText} />
          </View>
          <View style={styles(theme).calendarContainer}>
            <IconPosterNext color={isTheme ? 'rgba(239, 239, 239, 1)' : 'white'} />
          </View>
          <View style={styles(theme).calendarContainer}>
            <Calendar />
            {/* <View style={styles(theme).roundView}>
              <TextView text="2" style={styles(theme).digitText} />
            </View> */}
            <TextView text={strings.book_in.toLocaleUpperCase()} style={styles(theme).tapLinkText} />
          </View>
          <View style={styles(theme).calendarContainer}>
            <IconPosterNext color={isTheme ? 'rgba(239, 239, 239, 1)' : 'white'} />
          </View>
          <View style={styles(theme).struckContainer}>
            <Struck />
            {/* <View style={styles(theme).roundView}>
              <TextView text="3" style={styles(theme).digitText} />
            </View> */}
            <TextView text={strings.look_fresh.toLocaleUpperCase()} style={styles(theme).tapLinkText} />
          </View>
        </View>
        
      </ImageBackground>
    </View>
  );
};

export default withTheme(DigitalFlyar);

ADLsourceCode avatar Aug 12 '22 09:08 ADLsourceCode

@gre any update on this?

ADLsourceCode avatar Aug 22 '22 02:08 ADLsourceCode

it's very hard for me to dig into each single usecase, it would be good to pinpoint to the part of your component that don't capture, like having a minimal reproductible example would help. there are many limitations on cases where the native side can't properly capture views.

gre avatar Oct 02 '22 09:10 gre

i have the opposite result, is completely blank on iOS while not on android, and i got the same configuration and view for both

Vasault avatar Oct 12 '22 20:10 Vasault

Hello, same issue with android here as well.

I have the following component:

<>
      <ViewShot ref={viewShotRef} style={{flex: 1}}>
        <ViroARSceneNavigator
          ref={ref}
          autofocus={autofocus}
          initialScene={{
            scene: ARScene,
          }}
          style={{flex: 1}}
        />
      </ViewShot>
      <ARGui
        ref={viewShotRef}
        isLoading={isRealLoading}
        isProduct={!!productId}
      />
    </>

The ARGui is passing the viewShotRef to the "ARScreenshotButton" component when the capture method is called on button press.

const ARScreenshotButton = (_props: any, ref?: MutableRefObject<any>): JSX.Element => {
  const {showModal} = useModal();

  async function handleTakeScreenshot(): Promise<void> {
    try {
      // takeScreenshot from viro-react not working in v 2.23.0
      // using react-native-screenshot-capture instead
      const imageUrl = await ref.current?.capture();

      CameraRoll.save(imageUrl, {type: 'photo'});

      showModal({
        HeaderText: t`Info`,
        BodyText: t`Picture has been successfully stored to camera roll`,
        SuccessButtonText: t`Ok`,
      });
    } catch (err) {
      console.log(err);
    }
  }

  return (
    <Pressable onPress={handleTakeScreenshot}>
      <Camera />
    </Pressable>
  );
}

export default forwardRef(ARScreenshotButton);

On iOS picture is created perfectly, on android its just (blank) kind of white image.

I've didn't find any info that we should somehow change the Manifest xml or anything else. Is it necessary?

Thanks in advance for any help.

EDIT: According to the Interoperability Table react-gl wont work on android.

ViktorVojtek avatar Dec 19 '22 11:12 ViktorVojtek

@ViktorVojtek same issue here.. Did you find anything? (There is a prop that maybe helps but I am not sure on where to use it.. *handleGLSurfaceViewOnAndroid)

michalis-ligopsychakis avatar Apr 21 '23 12:04 michalis-ligopsychakis

same issue with imagebackground not showing the image in the screenshot.

I solved this issue by using imageRef on ImageBackground. Code is imcomplete as I tried to remove unneeded portions:

  const imageRef = useRef(null);

  const {captureScreenshot, moveFileToStorage} = useScreenshot({
    viewRef: imageRef,
  });

  const onScreenShotPress = async () => {
    const uri = await captureScreenshot(); // Capture the screenshot
  };

  return (
    <View style={styles.container}>
      <ImageBackground
        ref={imageRef}
        source={{uri: imageURL}}
        style={styles.imageBackground}>

      </ImageBackground>
import {captureRef} from 'react-native-view-shot';

export default function useScreenshot({viewRef}) {
  const captureScreenshot = async () => {
    if (!viewRef.current) {
      console.warn('viewRef.current is null. Skipping screenshot capture.');
      return null;
    }

    try {
      const uri = await captureRef(viewRef.current, {
        format: 'png',
        quality: 1,
      });
      console.log(`Screenshot captured: ${uri}`);
      return uri;
    } catch (error) {
      console.error('Failed to capture screenshot:', error);
      return null;
    }
  };

bruteforks avatar Apr 29 '23 22:04 bruteforks

just try to go with png format for both ios and android it will work <ViewShot captureMode="mount" options={{ fileName: 'file-name', format:"png", quality: 0.9, }} ref={ref}> <View style={[styles.shadow,{ width: 180, height: 50,

                      alignItems: 'center',
                    }]}>
                    <Text style={styles.tittle}>{signatoryName}</Text>
                  </View>
                </ViewShot>

madhuvenkat123 avatar Aug 23 '23 08:08 madhuvenkat123

In my case, The boxShadow on the view (or child of the view) I was capturing was causing this issue on Android. With the elevation set to any non-zero value, captureRef was capturing a blank image, probably because underneath there some issue while capturing components having box shadow or something.

I solved this by creating a state like: const [capturingScreenshot, setCapturingScreenshot] = useState(false);

Now when you capture the view shot, set to true, then back to false once finished:

const handleDownloadInvoice = async () => {
    setCapturingScreenshot(true);   // <-- HERE

    setTimeout(async () => {
      const localImageUri = await captureViewShot(contentRef, 'png');

      setCapturingScreenshot(false);      // <-- HERE

      CameraRoll.save(localImageUri)
        .then(res => {
          if (res) {
            showToast('Invoice saved!');
          }
        })
        .catch(err => {
          showToast('Could not download invoice.');
          console.log('SAVING TO CAMERAROLL: ', err);
        });
    }, 500);
  };

And adding condition while adding the boxShadow on Android using elevation: <View style={[ styles.receiptBackground, { elevation: capturingScreenshot ? 0 : 20 } ]}>

AbhayRajpootIndia avatar Mar 04 '24 13:03 AbhayRajpootIndia