Reputation: 2729
I have a query regarding tag. I want an image to take entire width of parent which I do using alignSelf:stretch, but I also want the height to be according to the aspect ratio of the image. How can I achieve something like this?
So I want a way to specify the height as a ratio of the width of the Image.
Upvotes: 106
Views: 176845
Reputation: 9
I created a custom component to adjust image's size based on a restriction, height or width, while maintaining the original aspect ratio, for images that can vary in its size and aspect ratio.
The issue i was encountering was I needed the image to take up to 60px in height and wanted to keep AR but the component (a card) can take images without standardised AR.
Not sure if that was the issue you were encountering. Using Image component from react-native I could achieve that by adding height: 60, width:140
in Image's style but that way the image would be centered which wasn't what I needed.
So I came up with this component and it worked perfectly:
import React, { useState, useEffect } from 'react';
import { Image } from 'react-native';
const DynamicImage = ({
picture_url,
sizeRestriction,
restrictionType,
setIsLoading,
}) => {
const [imgWidth, setImgWidth] = useState(sizeRestriction);
const [imgHeight, setImgHeight] = useState(sizeRestriction);
const [isLoadingImage, setIsLoadingImage] = useState(true);
useEffect(() => {
if (picture_url) {
Image.getSize(
picture_url,
(width, height) => {
restrictionType === 'height' &&
setImgWidth(Math.floor(sizeRestriction * (width / height)));
restrictionType === 'width' &&
setImgHeight(Math.floor(sizeRestriction * (height / width)));
setIsLoadingImage(false);
},
() => {
console.error(
'Unable to fetch image dimensions. Using default values.',
);
setIsLoadingImage(false);
},
);
} else {
setIsLoadingImage(false);
}
}, [picture_url]);
if (isLoadingImage) {
return null; // or render a loading indicator
}
return (
<Image
resizeMode={'contain'}
source={{ uri: picture_url }}
style={{
width: imgWidth,
height: imgHeight,
}}
onLoadEnd={() => setIsLoading(false)}
/>
);
};
export default DynamicImage;
Usage for fixed height of 60px:
<DynamicImage
picture_url={item.picture_url}
sizeRestriction={60}
restrictionType={'height'}
setIsLoading={setIsLoading}
/>
Hope this can help someone looking for the same issue I was having.
Upvotes: 0
Reputation: 13
let's create a component like this:
const ImageCustom = ({uri}) => {
const [ratio, setRatio] = useState(0);
Image.getSize(
uri,
(width, height) => {
setRatio(width / height);
},
error => {
console.log('error:', error);
},
);
return (
<Image
source={{uri: uri}}
style={{width: '100%', height: undefined, aspectRatio: ratio}}
/>
);
};
And call it like this
<ImageCustom uri={item}/>
Upvotes: 0
Reputation: 3049
Im my case on RN 0.68.0 on iOS 16 I had to do this in order to make the image show up properly in proper aspect ratio and fill the available width.
Setting aspect ratio directly on the <Image>
element did not work.
imageSource
is an object made from Image.getSize()
i.e. {uri, width, height}
// ....
{imageSource && (
<View
style={[
styles.imageContainer,
{
aspectRatio:
(imageSource.width ?? 1) / (imageSource.height ?? 1),
},
]}>
<Image source={imageSource} style={styles.image} />
</View>
)}
// ...
const styles = StyleSheet.create({
container: {
flex: 1,
paddingHorizontal: 2 * spacing,
},
imageContainer: {},
image: {
flex: 1,
width: '100%',
},
});
Upvotes: 0
Reputation: 36
Give aspectRatio and width to the parent view and add width and height 100% to the Image If you want an image with width of 100 and height of 50
<View style={{ width: 100, aspectRatio: 2 }}>
<Image
source={{ uri: '' }}
style={{
width: '100%',
height: '100%'
}}/>
</View>
Upvotes: -1
Reputation: 3157
I like bdv's approach and I use this kind of images almost everywhere in my app. That's why I created an own component which is using onLayout
to also support device rotation.
import resolveAssetSource from "resolveAssetSource";
import React, { useCallback, useState } from "react";
import { Image, View } from "react-native";
export default function FullWidthImage(props) {
const [width, setWidth] = useState(0);
const [height, setHeight] = useState(0);
const onLayout = useCallback((event) => {
const containerWidth = event.nativeEvent.layout.width;
if (props.ratio) {
setWidth(containerWidth);
setHeight(containerWidth * props.ratio);
} else if (typeof props.source === "number") {
const source = resolveAssetSource(props.source);
setWidth(containerWidth);
setHeight(containerWidth * source.height / source.width);
} else if (typeof props.source === "object") {
Image.getSize(props.source.uri, (w, h) => {
setWidth(containerWidth);
setHeight(containerWidth * h / w);
});
}
}, [props.ratio, props.source]);
return (
<View onLayout={onLayout}>
<Image
source={props.source}
style={{ width, height }} />
</View>
);
}
You can use it like this:
<FullWidthImage source={{ uri: "http://example.com/image.jpg" }} />
<FullWidthImage source={require("./images/image.jpg")} />
Or if you know the ratio like this:
<FullWidthImage source={{ uri: "http://example.com/image.jpg"}} ratio={0.5} />
<FullWidthImage source={require("./images/image.jpg")} ratio={0.5} />
Upvotes: 37
Reputation: 2464
With resizeMode='contain'
and flex=1
I get the image in full width while keeping the aspect ratio.
<Image
source={{ uri: 'URI' }}
resizeMode="contain"
style={{flex:1}} />
The image need to be in a container View with flex or height defined, so the flex of the image can work.
Upvotes: 5
Reputation: 2729
Use style={{ aspectRatio: 3/2 }}
for a horizontal image with width to height ratio of 3:2.
Docs: https://reactnative.dev/docs/layout-props#aspectratio
(Available in RN 0.40+)
Upvotes: 115
Reputation: 81
const RespImage = ({ offer }) => {
const [height, setHeight] = useState(0);
const [width, setWidth] = useState(0);
let image_url = `YOUR_IMAGE_URI`;
Image.getSize(image_url, (srcWidth, srcHeight) => {
const maxHeight = Dimensions.get('window').height;
const maxWidth = Dimensions.get('window').width;
const ratio = Math.min(maxWidth / srcWidth, maxHeight / srcHeight);
setHeight(srcHeight * ratio);
setWidth(srcWidth * ratio);
});
return (
<View>
<Image resizeMode={'contain'} style={{ height: height, width: width, marginBottom: 20, resizeMode: "contain" }}
source={{ uri: image_url }}
/>
</View>
)
}
Upvotes: 2
Reputation: 30360
In my case, I am using Styled Components in my React Native (v0.62+) project.
I needed to specify a square aspectRatio
for Image
components that had a defined width
and undefined height
.
I found that styling height:0;
achieved the "square image" result that I wanted:
// Gallery container styled-component
const Gallery = styled.View`
flexDirection:row;
flexWrap:wrap;
`
// Square half-width image styled-component
const Photo = styled.Image`
width:50%;
height:0;
aspectRatio:1;
`
This method also works for full width image styling - replacing width:50%
with width:100%
produces the expect result with correct aspect ratio of each image.
Upvotes: 2
Reputation: 20
Use resizeMode='contain'
<Image style={{height:'100%', width:'100%'}} resizeMode="contain" source={{uri:this.state.imgSource}} />
This will keep the original aspect ratio, with given width and height as max-height and max-width.
Upvotes: -3
Reputation: 79
In my case i also had to set height to 'auto' :
{
width: 200,
height: 'auto',
aspectRatio: 16 / 9,
}
Upvotes: 7
Reputation: 1204
It's actually pretty simple.
The Image
class has a getSize
method. [1]
Let's say that you've created a component for your aspectRatioImage
and you calculate the appropriate values every time componentWillMount
fires.
Then your code would look something like this:
componentDidMount() {
Image.getSize(this.props.source.uri, (srcWidth, srcHeight) => {
const maxHeight = Dimensions.get('window').height; // or something else
const maxWidth = Dimensions.get('window').width;
const ratio = Math.min(maxWidth / srcWidth, maxHeight / srcHeight);
this.setState({ width: srcWidth * ratio, height: srcHeight * ratio });
}, error => {
console.log('error:', error);
});
}
So now that the image height and width are saved in your component's state, you can just run
<Image
style={{ width: this.state.width, height: this.state.height }}
source={this.props.source}
resizeMode="cover"
/>
[1] - https://facebook.github.io/react-native/docs/image.html#getsize
Upvotes: 18
Reputation: 89
You can calculate the image height based on the width/height ratio.
So if the image originally is 200x100, after setting its resizeMode to stretch:
var deviceWidth: Dimensions.get('window').width;
...
myImage {
width: deviceWidth,
height: deviceWidth * 0.5
}
I know maybe it is not the best practice, but it helped me a lot with images of all sizes that needed to mantain a certain relation with other images, etc.
Upvotes: 6
Reputation: 4905
You can use react-native-scalable-image. The following example will do the job:
import React from 'react';
import { Dimensions } from 'react-native';
import Image from 'react-native-scalable-image';
const image = <Image width={Dimensions.get('window').width} source={{uri: '<image uri>'}} />;
Upvotes: 5
Reputation: 1285
<Image
source={require('../../assets/img/headers/image-1.jpg')}
style={styles.responsiveImage}
/>
const styles = StyleSheet.create({
responsiveImage: {
width: '100%',
// Without height undefined it won't work
height: undefined,
// figure out your image aspect ratio
aspectRatio: 135 / 76,
},
});
Upvotes: 111
Reputation: 494
Typically, doing the following would give us an image rendered to max width/height depending on orientation while maintaining the aspect ratio of the image itself:
render(){
return(<Image style={{'width': Dimensions.get('window').width,
'height': Dimensions.get('window').height}}
resizeMode='contain'
source='[URL here]'
</Image>);
}
Using 'contain' with resizeMode: Scale the image uniformly (maintain the image's aspect ratio) so that both dimensions (width and height) of the image will be equal to or less than the corresponding dimension of the view (minus padding).
Update: * Unfortunately, it seems that there is a common bug with resizeMode's 'contain' specifically when using react native for Android: https://github.com/facebook/react-native/pull/5738*
Upvotes: 5
Reputation: 489
I tried the Image.getSize approach, but had problems, since we gather all the image links in a config file and then pass the ImageURISource into the source prop of the Image.
My solution for that was to wait for the Image onLayout callback to get it's layout properties and use that to update the dimensions. I created a component for that:
import * as React from 'react';
import { Dimensions, Image, ImageProperties, LayoutChangeEvent, StyleSheet, ViewStyle } from 'react-native';
export interface FullWidthImageState {
width: number;
height: number;
stretched: boolean;
}
export default class FullWidthImage extends React.Component<ImageProperties, FullWidthImageState> {
constructor(props: ImageProperties) {
super(props);
this.state = { width: 100, height: 100, stretched: false };
}
render() {
return <Image {...this.props} style={this.getStyle()} onLayout={this.resizeImage} />;
}
private resizeImage = (event: LayoutChangeEvent) => {
if (!this.state.stretched) {
const width = Dimensions.get('window').width;
const height = width * event.nativeEvent.layout.height / event.nativeEvent.layout.width;
this.setState({ width, height, stretched: true });
}
};
private getStyle = (): ViewStyle => {
const style = [StyleSheet.flatten(this.props.style)];
style.push({ width: this.state.width, height: this.state.height });
return StyleSheet.flatten(style);
};
}
This will update the dimensions of the image to match the width of the screen.
Upvotes: 0
Reputation: 111
You may use something like this:
<View style={{height:200}} >
<Image source={require('image!MyImage') style={{ resizeMode:Image.resizeMode.ratio, flex:1 }}} />
</View>
Please not that you still have to set the height on the view container.
Upvotes: -9