I am working on an app which the app is intented to capture/load an image, then get the correct color from the touched coordinate on that image / camera view. (Similar to iOS's Swatches app)
The libraries I am using is react-native-camera, react-native-get-pixel-color and react-native-draggable .
There is some logic issue I'm stucked on currently....and I have researched from online and still lost in the jungle
Ratio of every devices is not same so I could not have hardcoded on the ratio, based on the document stated that the ratio is only available for android but not for iOS (default will be 4:3 for android if not defined any). So I'm thinking is it better to not to set it ? Rely on pictureSize better ? Or can without both ? Any advices is welcome...
The part which I'm quite confusing now is on the calculation part, I wanted to get the correct part of the image's color, despite different ratio , device's screen size and image size:
- Live view - always get from the center
- Still view - based on draggable and get the coordinates
renderCamView = () => {
if (this.state.isEnabled) {
return (
<Image
source={{ uri: this.state.liveImg.data.uri }}
style={styles.imgPreview}
/>
);
} else {
return (
<RNCamera
ref={(ref) => {
this.camera = ref;
console.log('isref');
}}
onCameraReady={()=> this.getCamData()}
style={{
flex: 1,
justifyContent: 'flex-start',
alignItems: 'center',
backgroundColor: colors.black
}}
type={RNCamera.Constants.Type.back}
flashMode={this.getFlashMode()}
exposure={this.state.camera.exposure}
whiteBalance={this.state.camera.whiteBalance}
//ratio={'1:1'}
// pictureSize={this.state.pictureSize}
captureAudio={false}
androidCameraPermissionOptions={{
title: 'Permission to use camera',
message: 'We need your permission to use your camera',
buttonPositive: 'Ok',
buttonNegative: 'Cancel',
}}
/>
);
}
};
renderCenterView = () => {
if(this.state.isEnabled){
const { liveImg, mainView } = this.state;
return <Draggable
x={(this.state.mainView.width/2)-20} y={(this.state.mainView.height/2)-20}
isCircle
onShortPressRelease={this.captureColor}
onDragRelease={this.onDragEvt}
minX={0}
minY={0}
maxX={this.state.mainView.width}
maxY={this.state.mainView.height}
>
<View
style={{ width:50, height:50,borderWidth:15, borderRadius: 50, borderColor:'#d1c1b6', opacity: 0.8 }}></View>
</Draggable>
}else{
return <TouchableOpacity
onPress={this.captureColor}
style={[styles.middlePoint, { top:(this.state.mainView.height/2)-20, left:(this.state.mainView.width/2)-20 }]}/>
}
}
// Logic on Draggable, to find the coordinates on image view
onDragEvt = (event, gestureState, bounds) => {
let sourceWidth = this.state.liveImg.width;
let sourceHeight = this.state.liveImg.height;
// Find view dimensions
let width = this.state.mainView.width;
let height = this.state.mainView.height;
// Assuming image has been scaled to the smaller dimension to fit, calculate the scale
let wScale = width / sourceWidth;
let hScale = height / sourceHeight;
let scale = Math.min(wScale, hScale);
// Find the offset in the direction the image doesn't fill the screen
// For ImageViews that wrap the content, both offsets will be 0 so this is redundant
let offsetX = 0;
let offsetY = 0;
if (wScale <= hScale) {
offsetY = height / 2 - (scale * sourceHeight) / 2;
} else {
offsetX = width / 2 - (scale * sourceWidth) / 2;
}
// Convert event coordinates to image coordinates
let sourceX = (gestureState.moveX - offsetX) / scale;
let sourceY = (gestureState.moveY - offsetY) / scale;
if (sourceX < 0) {
sourceX = 0;
} else if (sourceX > sourceWidth) {
sourceX = sourceWidth - 5;
}
if (sourceY < 0) {
sourceY = 0;
} else if (sourceY > sourceHeight) {
sourceY = sourceHeight - 5;
}
this.setState({ dragView: { x: sourceX, y: sourceY } });
};
getPixelByPercentage(percentage, widthOrHeight) {
return (percentage / 100) * widthOrHeight;
}
getPixel(imageData, x, y) {
try {
GetPixelColor.setImage(isIOS ? imageData.uri : imageData.base64).catch(
(err) => {
// Handle errors
console.error(err);
},
);
GetPixelColor.pickColorAt(x, y)
.then((color) => {
// from react-native-get-pixel-color
// 'color' return as HEX
})
.catch((err) => {
// Handle errors
console.error(err);
});
} catch (e) {}
}
captureColor = async () => {
if (this.state.isEnabled) {
// live view
const { dragView, liveImg } = this.state;
if (Object.keys(dragView).length !== 0) {
let sourceX = dragView.x;
let sourceY = dragView.y;
this.getPixel(liveImg.data, sourceX, sourceY);
} else {
let getPiX = this.getPixelByPercentage(50, liveImg.width);
let getPiY = this.getPixelByPercentage(50, liveImg.height);
this.getPixel(liveImg.data, getPiX, getPiY);
}
} else {
if (this.camera) {
const data = await this.camera.takePictureAsync(CAM_OPTIONS);
await Image.getSize(data.uri, (width, height) => {
console.log('W: ', width);
console.log('H: ', height);
this.setState({
capImage: {
data: data,
imageBase64: data.base64,
width: width,
height: height,
},
});
}).then((res) => {
const { capImage } = this.state;
let getPiX = this.getPixelByPercentage(50, capImage.width);
let getPiY = this.getPixelByPercentage(50, capImage.height);
this.getPixel(capImage.data, getPiX, getPiY);
});
}
}
};
NOTES:
1. renderCamView() - render either Camera or Image
2. renderCenterView() - render the center item, either is draggable in 'still' view or fixed touchable button in 'live' view to always get the center point
3. onDragEvt() - the Draggable's event to keep track the draggable item's movement
4. this.state.mainView - the state that holds width and height of the root <View /> of this screen
5. captureColor() - onPress event to get the pixel color
6. this.state.isEnabled - define it is still view / live view
Still view - (Capture and display as image / image select from album)
*Sorry for my bad English, if there is any confusing part please let me know