I want to set the width of an Image component to be 100% of my container's width and the height to be set according to the result width value and image's aspect ratio.
My container has some padding, which means that Dimensions.get('window').width is not useful to me since it does not gives me the value that results from image's width being set to 100%. Other scenarios also can't afford the Dimensions API as a acceptable solution.
Here's an code example:
<View
style={{
flex: 1,
paddingHorizontal: 20,
}}>
<Image
style={{
flex: 1, // Not what I want => just so I can see the image
width: '100%',
// height: 'auto', // The behavior that I want
backgroundColor: 'red',
}}
resizeMode={'contain'}
source={{
uri: 'https://via.placeholder.com/200x400/000000',
}}
/>
</View>
Result:
The red background serves just to visualize that the Image component.
Using onLayout prop I can get the runtime value of the resulting width, and with the aid of the Image API i could calculate the desired height. The problem with that is that React Native will first render a 0 height component and just after that onLayout seems to be resolved, and that makes sense.
So?
Is there a way to get before hand the width value of a image component such as this (declared using relative width)?
Is automatically setting image height even possible in React Native? If so, how?
Notes:
- In the image docs of React Native there is an explanation of why they chose not to size image components automatically by default.
- At least in this case, Parent onLayout is resolved after its children.
In your componentDidMount you could calculate the image metrics, and then use that height and width to calculate the aspectRatio.
componentDidMount = () => {
Image.getSize(YourUri, (width, height) => {
this.setState({width, height});
});
}
In your image you will add the styles like this:
<Image
style={{
width: '100%',
backgroundColor: 'red',
aspectRatio: this.state.height !== null ? this.state.width / this.state.height : 1
}}
resizeMode={'contain'}
source={{
uri: 'https://via.placeholder.com/200x400/000000',
}}
/>
As stated in React native docs:
Aspect ratio controls the size of the undefined dimension of a node.
Aspect ratio is a non-standard property only available in React Native
and not CSS.
-On a node with a set width/height aspect ratio controls the size of the unset dimension
-On a node with a set flex basis aspect ratio controls the size of the node in the cross axis if unset
-On a node with a measure function aspect ratio works as though the measure function measures the flex basis
-On a node with flex grow/shrink aspect ratio controls the size of the node in the cross axis if unset
Aspect ratio takes min/max dimensions into account
Related
I am trying to decrease the height of Image component in Next.js. But, When I increase the width, the height has no affect. The image height stays the same as width. If width is 100, the image becomes 100x100 but, I need 100x50. How can I achieve that?
Below is my code
<Image
alt="Mountains"
width={80}
height={50}
src={Dog}
style={{ objectFit: 'contain' }}
/>
I can't get resizeMode='contain' to work for images loaded remotely, wondering if there's something I've done wrong.
Render Method:
return (
<View style={styles.internal_button_avatar_container}>
<Image
style={styles.internal_button_avatar_image}
source={{ uri: this.state.imageURL }}
loadingIndicatorSource={require('../assets/icons/PageLink-Loading.png')}
resizeMode={'contain'}
/>
</View>
);
Styles (relevant section):
internal_button_avatar_container: {
marginRight: 10,
width: 40,
height: 40,
},
internal_button_avatar_image: {
width: 40,
height: 40,
},
I've tried resizeMode='contain' and resizeMode='center' but the image always is cropped around the outside of it's frame. Screenshot showing cropped icon.. I've also tried with undefined height and width on the image.
The cropping only appears to happen if the source image is not the same aspect ratio as the frame, a square source will result in perfectly scaled image.
I tried to reproduce your situation, but I haven't problems with that in this snack:
https://snack.expo.io/#gersonmontenegro/resizemode
The original size of the image is bigger than the container, even it has a different aspect radio, and it has loaded according to the internal_button_avatar_image.
Is ok for you?
Turns out I was getting the image from an endpoint in an API that auto-cropped the original image - should've checked the source image before everything else!
I use a FlatList component for images grid because it have a good performance:
<FlatList
data={photos}
columnWrapperStyle={styles.listColumn}
numColumns={4}
renderItem={photo => this.renderPhoto(photo)}
/>
For now renderPhoto func return a new FastImage component (i use it because it have a cool caching feature)
<FastImage
resizeMode={FastImage.resizeMode.cover}
source={{uri: photo.src}}
/>
In the end I have something like this:
But now I want to have a very familiar possibility. Tap on the image will start the animation after which the image will be stretched to the full width of the screen.
After that, the user can do the following:
Swipe left/right to see prev/next images from the FlatList
Zoom current image
Tap on image to show/hide control elements (footer and header)
Swipe up/down for closing carousel and return to the grid component
It might look something like this:
So, whats a problem?
All existing carousel solutions are a wrapper for an image collection. But I cant pass wrapper component inside FlatList.
I could not find a ready-made component for solving such a common problem.
There are some that I try to combine (Lightbox, Carousel). But this solution will greatly affect performance. (I need to load the entire collection of images from the FlatList into the carousel component) In addition, such solutions usually have problems with animation.
So I'm wondering if there really is no react-native solution for such a popular image view mechanics?
Perhaps it is worth making a native module on the swift/objc (FlatList of images with carousel modal)?
Actually is possible with the elements that you have.
First you have the carousel (react-native-looped-carousel):
const activeProps = {
resizeMode: 'contain',
flex: 1,
width: null
};
const { height, width } = Dimensions.get('window');
const renderCarousel = (album, currentPage) => (
<Carousel style={{ width, height }} currentPage={currentPage} autoplay={false}>
{album.map(image => (
<FastImage
style={{ flex: 1 }}
resizeMode={FastImage.resizeMode.contain}
source={{ uri: image.uri }}
key={image.uri}
/>))
}
</Carousel>
);
Then FastImage (react-native-fast-image) with the lightbox (react-native-lightbox):
LightImage = (props) => {
const currentPage = this.items.findIndex(x => x.uri === props.source.uri);
return (
<Lightbox
activeProps={activeProps}
renderContent={() => renderCarousel(this.items, currentPage)}
>
<FastImage {...props} />
</Lightbox>
);
}
Now you can use your renderItem with the component for the FastImage and Lightbox
<FlatList
data={this.items}
columnWrapperStyle={styles.listColumn}
numColumns={4}
renderItem={photo => this.LightImage(photo)}
/>
I've copied part of my code, so it won't work with just copy and paste. If you have any question feel free to ask!
There's only one problem with this implementation that if you rotate the device the layout breaks
Hello World,
I trying to crop an image like explain on the React-native Doc
<Image source={{uri: this.props.image, crop: {left: 50, top: 50, width: 100, height: 100}}} style={{height: 100, width: 100}}/>
But it's doesn't work the image is not cropped.
Any idea?
From the docs:
On the infrastructure side, the reason is that it allows us to attach metadata to this object. For example if you are using require('./my-icon.png'), then we add information about its actual location and size (don't rely on this fact, it might change in the future!). This is also future proofing, for example we may want to support sprites at some point, instead of outputting {uri: ...}, we can output {uri: ..., crop: {left: 10, top: 50, width: 20, height: 40}} and transparently support spriting on all the existing call sites.
React Native Image is not currently supporting image cropping, at least not the way you pointed, however you still have other options that will do the same job.
ImageEditor:
React Native Component, again from the docs:
Crop the image specified by the URI param. If URI points to a remote
image, it will be downloaded automatically. If the image cannot be
loaded/downloaded, the failure callback will be called.
Cropping doesn't require linking.
Image Crop Picker another package that offers cropping, but in a different way: Picking. Requires linking, but thankfully it also supports RN versions > 0.40.
I haven't used any of them, but if I were you, I would first try Image Editor, since you don't need any additional installation except importing.
you can use package for cropping the image. first package for uploading image and second for cropping the image.
react-native-image-picker
react-native-image-crop-picker
for uploading image you can use first package function like this
const pickImage = () => {
launchImageLibrary({quality: 0.5}, response => {
if (!response.didCancel) {
setUri(response.assets[0]);
setCropImg('');
}
});
};
for cropping the image you can use second package function like this
const Crop_img = () => {
ImagePicker.openCropper({
path: uri.uri,
width: dimensions.width - 30,
height: dimensions.height / 3.5,
maxFiles: 1,
showCropFrame: false,
}).then(image => {
console.log(image.path);
setCropImg(image.path);
});
};
I created an imageView in Titanium. Whenever I set the width and height of the image to "auto"; the image is not visible. But if I manually set height and width of the image, the image appears.
Why does this happen ?
var image = Titanium.UI.createImageView({
backgroundImage:'test.png',
width: 'auto',
height: 'auto'
})
If you go here on the DOCs you will see that `auto is deprecated:
'auto' represents the default sizing behavior for a given type of view. The use of 'auto' is deprecated, and should be replaced with the SIZE or FILL constants if it is necessary to set the view's behavior explicitly.
Instead try setting the width and height to Titanium.UI.SIZE, Titanium.UI.FILL, a pixel value, or a percentage of the parent view.