Get coordinates of touch event relative to Image - react-native

I have an image centered on the screen. I need to make it touchable and I need to get the coordinates of the touch event relative to the image. I have wrapped my image in a TouchableOpacity to make it touchable. The problem is that the touch coordinates are relative to the TouchableOpacity and not the Image. The TouchableOpacity is taking up the entire screen but the Image is centered inside it.
I either need to make my TouchableOpacity the same size as the Image, or I need to know the offset of the Image within the TouchableOpacity.
I have tried using OnLayout and the NativeEvent object to get the position of the Image within it's parent but it just returns 0,0.
const {width, height} = Dimensions.get("window");
class Inspection extends React.Component {
handlePress(evt) {
// do stuff
...
}
render() {
return (
<View style={{flex:1, backgroundColor:'#fff'}}>
<TouchableOpacity
onPress={(evt) => this.handlePress(evt)}
style={{backgroundColor: '#3897f0'}}
>
<Image
onLayout={({nativeEvent}) => {
console.log(nativeEvent.layout);
}}
source={require('../../images/wireframe-car.jpg')}
resizeMode='contain'
style={{
maxHeight: height,
maxWidth: width
}}
/>
</TouchableOpacity>
</View>
);
}
}
Console Output:
{height: 683.4285888671875, width: 411.4285583496094, y: 0, x: 0}
I've added a backgroundColor to TouchableOpacity so you can see that it takes up the entire screen.
Is there another way of doing this?

TouchableOpacity would be the same size as of the Image because you haven't given any height to it, you simply need to assign onLayout prop to your TouchableOpacity like this
<View
style={{ flex: 1, backgroundColor: '#fff', alignItems: 'center', justifyContent: 'center' }}>
<TouchableOpacity
onLayout={({ nativeEvent }) => {
console.log(nativeEvent.layout)
}}
style={{}}
onPress={evt => this.handlePress(evt)}>
<Image
source={require('../../images/wireframe-car.jpg')}
resizeMode="contain"
style={{
maxWidth: '100%',
}}
/>
</TouchableOpacity>
</View>
This will give you the exact x and y of Image.
Update
The problem is also with image size, since the image size is quite big so it takes the height of the device and we get x:0 and y:0, in order to resolve this issue we can give the Image component a static height or calculate its height according to width. We can get width and height image from local path like this:
let imageUri = Image.resolveAssetSource(require('../../images/wireframe-car.jpg').uri)
Image.getSize(imageUri , (width, height) => {
console.log(`The image dimensions are ${width}x${height}`);
}, (error) => {
console.error(`Couldn't get the image size: ${error.message}`);
});

Related

How to make expo-av video to take needed inside a flatlist?

I am developing an instagram-like app where I needed to render images/videos. I am using flatlist to prevent memory lost, and using expo-av package to render the video.
Here is a screenshot of what I want to achieve:
So my goal here is to render videos with original ratio.
However, I am struggling to render the flatlist that contains the videos, it just doesn't render the component at all but I can still hear the video playing.
This is my FlatList:
<FlatList
style={{ width: "100%", height: "100%", backgroundColor: "yellow" }}
data={[0, 1, 2, 3, 4]}
keyExtractor={item => item}
renderItem={renderItem}
/>
And my renderItem callback:
const renderItem = ({ item }) => {
return <Post post={item} />;
}
Post item code:
export default ({ post }) => {
const videoRef = useRef(null);
const [status, setStatus] = useState({});
return (
<View style={{ width: "100%", height: "100%", backgroundColor: "blue" }}>
<Video
ref={videoRef}
source={{
uri: 'http://commondatastorage.googleapis.com/gtv-videosbucket/sample/VolkswagenGTIReview.mp4',
}}
style={{ width: "100%", height: "100%", backgroundColor: "blue" }}
resizeMode="contain"
autoplay
isLooping
shouldPlay={true}
onPlaybackStatusUpdate={status => setStatus(() => status)}
/>
</View>
);
}
Result (yellow background: flatlist area, post item should appear blue but not showing up):
The video would display if I give it a static width and height instead of values like 100%, but since I needed the renderItem to look original and take as much space as needed across all kinds of devices, so the only thing I could think of is a percentage.
If there is a way to know the aspect ratio or the width and height of the video, I can do dynamic calculations to achieve my goal, but I don't know if expo-av provide this information~
The renderItem would automatically take the max width inside a FlatList, but the height is default 0.
I figured that we can pass the video's natural aspect ratio to the style property so that it renders itself naturally with max size.
To get the video's natural aspect ratio, we have to define a function for the video's onReadyForDisplay property, it provides information of the video once the first frame is loaded.
To do that, we set the default ratio to the screen ratio:
import { Dimensions } from "react-native";
const defaultScreenRatio = Dimensions.get("window").width / Dimensions.get("window").height;
And then inside the component:
// Use the screenDefaultRatio to render the video before the video is loaded
const [videoRatio, setVideoRatio] = useState(screenDefaultRatio);
// Update the videoRatio right after we know the video natural size
const updateVideoRatioOnDisplay = (videoDetails) => {
const { width, height } = videoDetails.naturalSize;
const newVideoRatio = width / height;
setVideoRatio(newVideoRatio);
}
Code for Video item:
<View>
<Video
ref={videoRef}
source={{
uri: 'http://commondatastorage.googleapis.com/gtv-videosbucket/sample/VolkswagenGTIReview.mp4',
}}
style={{ aspectRatio: videoRatio, backgroundColor: "blue" }}
resizeMode="contain"
autoplay
isLooping
shouldPlay={true}
onPlaybackStatusUpdate={status => setStatus(() => status)}
// Update the video Ratio once done loading the first frame of the video
onReadyForDisplay={updateVideoRatioOnDisplay}
/>
</View>
So, I don't know how to make it dynamic to rach video, but you just can't have percentages for height and width

Overlapping items in React Native FlatList

I'm trying to make a list of items in FlatList overlap over each other like a stack of cards, but using a negative margin the item gets cut off, using "left: -20" does as well.
The image component is rather simple with round border:
export default class ProfilePicture extends React.Component {
render () {
let size = this.props.size || 50
return (
<Image
source={{ uri: this.props.picture }}
style={{
backgroundColor: 'rgba(12, 94, 20, 0.5);',
width: size,
height: size,
borderRadius: size / 2
}}
/>
)
}
}
And in the list is where I try to accomplish the overlap:
export default class RidersListCompact extends Component {
state = {
users: []
}
...
renderItem = ({ item: user, index }) => {
return <View style={styles.itemContainer}>
<ProfilePicture
picture={user.picture}
size={Layout.window.hp(6)}
/>
</View>
}
render () {
return (
<FlatList
renderItem={this.renderItem}
data={this.state.users}
keyExtractor={(user) => 'user_' + user.id}
horizontal
inverted
style={{ ...styles.container, ...this.props.style }}
/>
)
}
}
const styles = StyleSheet.create({
container: {
flexDirection: 'row-reverse'
},
itemContainer: {
marginRight: -Layout.window.hp(2),
width: Layout.window.hp(6),
height: Layout.window.hp(6),
backgroundColor: 'rgba(0,0,0,0);'
}
})
I tried setting different zIndex on each item but haven't had much luck, is there a way to overlap images/components in FlatList?
Cheers!
Make use of Flex. seperate Items by putting then in flex direact row wise. use Props from flex. Flex has following props available
alignContent
alignItems
alignSelf
aspectRatio
borderBottomWidth
borderEndWidth
borderLeftWidth
borderRightWidth
borderStartWidth
borderTopWidth
borderWidth
bottom
direction
display
end
flex
flexBasis
flexDirection
flexGrow
flexShrink
flexWrap
height
justifyContent
left
margin
marginBottom
marginEnd
marginHorizontal
marginLeft
marginRight
marginStart
marginTop
marginVertical
maxHeight
maxWidth
minHeight
minWidth
overflow
padding
paddingBottom
paddingEnd
paddingHorizontal
paddingLeft
paddingRight
paddingStart
paddingTop
paddingVertical
position
right
start
top
width
zIndex
If you want to overlap images you should use position style in your styles. You need to set position to absolute and set left, right, top, bottom values.
More information

React-Native-Camera Limit Camera Area

I have some problems limiting the Area of a RNCamera to its surrounding view.
All related tickets I found here did not provide a proper solution with RNCamera.
My Component has flex column design with two views.
View 1: as a wrapper for the camera
View 2: results view, colored in red with opacity
As you can see the RNCamera goes over the bounds of the surrounding view.
This leads causes problems while Barcode Scanning.
The barcode in the example screenshot is detected.
Is it possible to limit the react native camera to the bounds of its surrounding view?
Or if not is there a way to see the coordinates of the scanned barcode and compare it against the measurements of the Views.
The scanned barcode has bounds but I am not sure how to translate that to a proper position.
const Stocktake = () => {
let redBoxView, cameraWrapperView, camera
function onBarCodeRead(bc) {
console.log('bc', bc)
if (redBoxView) {
redBoxView.measure((x, y, width, height, pageX, pageY) => {
console.log('RedBox', x, y, width, height, pageX, pageY);
console.log('X', pageX)
console.log('Xend', pageX + width)
console.log('Y', pageY)
console.log('Yend', pageY + height)
})
}
}
return (
<View style={{flex: 1}}>
<View style={{ backgroundColor: "purple", flex: 1 }}
ref={view => {
cameraWrapperView = view
}}>
<RNCamera
ref={ref => {
camera = ref;
}}
defaultTouchToFocus
mirrorImage={false}
onBarCodeRead={(bc) => { onBarCodeRead(bc) }}
onFocusChanged={() => { }}
onZoomChanged={() => { }}
style={styles.preview}
playSoundOnCapture={true}
/>
</View>
<View style={{ backgroundColor: "rgba(254,0,0,0.6)", flex: 2, alignItems: "flex-end", justifyContent: "flex-end" }}
ref={ref => {
redBoxView = ref;
}}>
{redBoxView ? <Text>{JSON.stringify(redboxMeasures)}</Text> : null}
</View>
</View>
);
}

Image Resize Cover - Keep Top Left of Image

I have a big image that needs to be cropped on certain devices, but it's very important that the top left of my image stays in tact because it has some important content.
<View style={{flex:1}}>
<Image source={MY_IMAGE}
resizeMode="cover"
style={{
flex: 1.8,
width: null,
height: null
}}
/>
<View style={{ flex: 1 }}>
//other content
</View>
</View>
^This is very close to what I want, but by default it looks like resizeMode:"Cover" just zooms in on the center of the image, cutting off my important content.
Is there any way to complete the image resizing based on something like x:0, y: screenHeight so that it keeps the top left of my image and resizes from the bottom right?
Import necessary packages
import { StyleSheet, Image, Dimensions } from 'react-native';
Your img element
<Image style={styles.images} resizeMode="stretch" source={{ uri: 'src here' }} />
Add this styles at the bottom
const styles = StyleSheet.create({
images: {
width: Dimensions.get('window').width,
height: Dimensions.get('window').width / 2
}
});
Try this the image width will be dynamically set even when you rotate your device.

React Native: print full image in a Scrollview

I have an image that is very tall, but not very large. I want to print it in a scrollview, so you can see it all on one screen, but nothing works.
after playing with widths and heights and flexs for my scrollview and my image, the best result I get is when I have on of them with style={{width='100%'}}, like that
<ScrollView style={{width: '100%'}}>
<Image
source={require('./assets/skeleton/fullSkeleton.png')}
resizeMode='cover'
/>
</ScrollView>
which prints the image at full width, but the scrollview's height is the original image's height, which doesn't let me see the whole resized image.
Here, a bad drawing to represent my image, and what I want to see on my phone screen
EDIT:
With this bit of code:
<ScrollView
contentContainerStyle={{alignItems: 'center'}}>
<Image
source={require('./fullSkeleton.png')}
resizeMode='cover'
/>
</ScrollView>
I end up with
Which can be scrolled down to see the rest of the Image. As you can see, it can be navigated vertically, but doesn't take the whole screen's width
This kind of thing in Reat Native is actually quite tricky. But you can do it easily; First get picture dimensions:
const FullWidthPicture = ({ uri }) => {
const [ratio, setRatio] = useState(1);
useEffect(() => {
if (uri) {
Image.getSize(uri, (width, height) => {
setRatio(width / height);
});
}
}, [uri]);
return (
<Image
style={{ width: '100%', height: undefined, aspectRatio: ratio }}
resizeMode="contain"
source={{ uri }}
/>
);
};
And use it like this:
<ScrollView style={{flex: 1}}>
<FulWidthImage uri={pictureUri} />
</ScrollView>
The following may help:
You can use contentContainerStyle to style the contents of the ScrollView. Like this (essentially what you have):
<ScrollView
contentContainerStyle={{alignItems: 'center'}}>
<Image
source={require('./fullSkeleton.png')}
resizeMode='cover'
/>
</ScrollView>
That works for me - I can scroll down the whole image and at the bottom it stays visible.
NB: To make the image cover the whole screen while maintaining the height required, you will need to set the width specifically. In my case on iPhone 7+ that meant adding style={{width: 414}} to the Image component AND changing resizeMode to 'stretch'.
Here is the official take on Image resizing...and getting device dimensions will be useful here too.