React Native - add a masked circle overlay over image
Asked Answered
P

3

9

How do I go about adding an opaque circular overlay over an image in React Native? Similar to the instagram image picker:

enter image description here

as trivial a task this may seem, I've had a world of trouble replicating this. Any suggestions?

Plurality answered 9/5, 2021 at 0:9 Comment(5)
Maybe look at github.com/ivpusic/react-native-image-crop-picker or github.com/react-native-masked-view/masked-view. I don't know if react-native-image-crop supports the transparent background part though.Tubb
@BasvanderLinden I'm using Expo so react-native-image-crop-picker is not an option. I tried using masked-view but can't seem to get the logic right.Plurality
You could try docs.expo.io/versions/latest/sdk/imagepicker in that case. It allows you to crop images when selecting them from the gallery. Only seems to be able to crop a rectangular area. If you wanted to have the overlay when selecting the image from the gallery (custom behavior) you probably would have to deal with native modules yourself. But you could also leave that up to expo image picker and after selecting the image from the gallery display the image with an overlay on the page and adjust the image from there with masked-view.Tubb
Is that last approach with the masked view sufficient for you? That would make the question easier to answer.Tubb
@BasvanderLinden I've implemented my own image picker modal from scratch using the MediaLibrary lib. Sure, if I knew how to solve this using masked view it would most definitely be suffice.Plurality
N
6

As someone mentioned in the comments, the way to achieve this is with React Native Masked View.

Install it in your project by running:

npm install -S @react-native-community/masked-view

or

yarn add @react-native-community/masked-view

Then you can use it as follows. I've adapted the example from their README for you here:

import MaskedView from '@react-native-community/masked-view';
import React from 'react';
import { View } from 'react-native';

export default class App extends React.Component {
  render() {
    return (
      <View
        style={{
          flex: 1,
          backgroundColor: '#000000', // "Edge" background
          maxHeight: 400,
        }}
      >
        <MaskedView
          style={{ flex: 1 }}
          maskElement={
            <View
              style={{
                // Transparent background mask
                backgroundColor: '#00000077', // The '77' here sets the alpha
                flex: 1,
              }}
            >
              <View
                style={{
                  // Solid background as the aperture of the lens-eye.
                  backgroundColor: '#ff00ff',
                  // If you have a set height or width, set this to half
                  borderRadius: 200,
                  flex: 1,
                }}
              />
            </View>
          }
        >
          {/* Shows behind the mask, you can put anything here, such as an image */}
          <View style={{ flex: 1, height: '100%', backgroundColor: '#324376' }} />
          <View style={{ flex: 1, height: '100%', backgroundColor: '#F5DD90' }} />
          <View style={{ flex: 1, height: '100%', backgroundColor: '#F76C5E' }} />
          <View style={{ flex: 1, height: '100%', backgroundColor: '#2E6D3E' }} />
        </MaskedView>
      </View>
    );
  }
}

Newsboy answered 24/5, 2021 at 13:15 Comment(1)
perfect, thanks I previously tried to use masked view but I was a but off with the structure. This did it.Plurality
D
0
import React from 'react';
import {
  SafeAreaView,
  StyleSheet,
  View,
  Image,
  Text
} from 'react-native';

const Test = () => {
  return (
    <SafeAreaView style={{flex: 1}}>
      <View style={styles.container}>
        <Image
          source={{
            uri: 'https://raw.githubusercontent.com/AboutReact/sampleresource/master/old_logo.png'
          }}
          //borderRadius will help to make Round Shape
          style={{
            width: 200,
            height: 200,
            borderRadius: 200 / 2
          }}
        />
        <Text style={styles.textHeadingStyle}>
          About React
        </Text>
      </View>
    </SafeAreaView>
  );
};

const styles = StyleSheet.create({
  container: {
    flex: 1,
    justifyContent: 'center',
    alignItems: 'center',
    backgroundColor: '#e0dcdc',
  },
  textHeadingStyle: {
    marginTop: 30,
    fontSize: 40,
    color: '#0250a3',
    fontWeight: 'bold',
  },
});

export default Test;
Deliberate answered 20/5, 2021 at 12:23 Comment(1)
This doesn't really answer the question though, does it? And you've just copy pasted this from here right? Please give credit to the author by including a link in the answer to the article where you got the code from.Tubb
D
-4

import React, { Component } from 'react';
import {
  View,
  StyleSheet,
  Text,
  ScrollView,
  TouchableOpacity,
} from 'react-native';
import styles from './styles';
import { Circle, CustomHeader, CustomImage, CTNexaBold } from '../../components';
import translate from '../../translations/translate';
import { images, icons } from '../../assets'
import { widthPercentageToDP as wp, heightPercentageToDP as hp } from 'react-native-responsive-screen';
import utils from '../../utils';
import { Colors } from '../../common';
import ImagePicker from 'react-native-image-crop-picker';

class UploadProfilePicture extends Component {
  constructor(props) {
    super(props);
    this.state = {
      profileImage: '',
      isProfileImage: false,
    };
  }

  componentDidMount() {

  };

  changeProfilePhoto() {
    ImagePicker.openPicker({
      width: 300,
      height: 400,
      cropping: true
    }).then(image => {
      this.setState({
        profileImage: image.path,
        isProfileImage: true,
      })
    });
  }

  render() {
    const { profileImage, isProfileImage } = this.state
    return (
      <View style={styles.container}>
        {utils.statusBar('dark-content', Colors.white)}
        <CustomHeader
          title={<CTNexaBold customStyle={styles.customStyle} >{translate("Upload Profile Picture")}</CTNexaBold>}
          {...this.props}
        />
        <View style={{ flex: 0.8, alignItems: 'center', justifyContent: 'center', marginBottom: 200 }} >
          <View>
            <Circle
              width={wp('44%')}
              height={wp('44%')}
              borderRadius={wp('44%')}
              borderColor={'#A28A3D'}
              marginVertical={40}
              marginHorizontal={70}
            >
              <CustomImage
                style={styles.userAvatar}
                // source={images.iconProfile}
                source={ isProfileImage ?  { uri: profileImage } : images.iconProfile }
              />
            </Circle>
          </View>
          <View style={{ marginHorizontal: wp('10%') }} >
            <TouchableOpacity onPress={()=>this.changeProfilePhoto()} >
              <View style={{ flexDirection: 'row', justifyContent: 'space-between' }} >
                <CTNexaBold customStyle={styles.profileText} >Change Profile Photo</CTNexaBold>
              <CustomImage
                style={styles.containerCustomImage}
                source={icons.arrowRight}
                />
              </View>
            </TouchableOpacity>
          </View>
        </View>
        <View style={{ flex: 0.2, alignItems: 'center', justifyContent: 'center', marginBottom: 20 }} >
          <TouchableOpacity style={styles.saveButton} >
            <CTNexaBold customStyle={styles.saveButtonText} >SAVE</CTNexaBold>
          </TouchableOpacity>
        </View>
      </View>
    );
  }
}


export default UploadProfilePicture;
Deliberate answered 20/5, 2021 at 14:12 Comment(2)
this is from my running project, in style prop you can use overflow:hidden its optionalDeliberate
your using custom components in which there is no reference to their logic...Plurality

© 2022 - 2024 — McMap. All rights reserved.