Reputation: 1443
I am trying to display list of images in a scrollview. Width should be 100%, while height should be automatic, keeping aspect ratio.
The searches I did pointed to various solutions which give fullscreen background style.
const styles = StyleSheet.create({
image: {
width: null,
height: 300,
resizeMode: 'cover'
}
});
<ScrollView style={{flex: 1}}>
<Image style={styles.image} source={require('../../../images/collection-imag1.png')}/>
<Image style={styles.image} source={require('../../../images/collection-imag2.png')}/>
</ScrollView>
I have tried various combinations of width: null, height: null, flex: 1, alignSelf etc. The above solution is almost working, except the height is not dynamic. Parts of the image are not visible.
Upvotes: 123
Views: 216415
Reputation: 829
I've found a solution for width: "100%", height: "auto" if you know the aspectRatio (width / height) of the image.
Here's the code:
import { Image, StyleSheet, View } from 'react-native';
const ResponsiveImage = () => (
<View style={styles.imgContainer}>
<Image style={styles.image} source={require('assets/images/image.png')} />
</View>
);
const style = StyleSheet.create({
imgContainer: {
flexDirection: 'row'
},
image: {
resizeMode: 'contain',
flex: 1,
aspectRatio: 1 // Your aspect ratio
}
});
This is the most simplest way I could get it to work without using onLayout
or Dimension
calculations. You can even wrap it in a simple reusable component if needed. Give it a shot if anyone is looking for a simple implementation.
Upvotes: 62
Reputation:
Try using Image.getSize(uri, (width, height) => {console.log(width, height);});
.
const Image = url => {
const [aspectRatio, setAspectRatio] = useState(1.2);
Image.getSize(
url,
(width, height) => {
setAspectRatio(width / height);
},
);
return (
<Image
source={{
uri: url
}}
style={{width: '100%', aspectRatio: aspectRatio}}
resizeMode="cover"
/>
);
};
Upvotes: 0
Reputation: 679
I took Jon James' idea and applied it on the image itself, instead of the image's container. This is what I ended up using, and it works great:
import { useState, useEffect } from 'react';
import { Image } from 'react-native';
export default function AutoHeightImage({ uri, style }) {
const [paintedWidth, setPaintedWidth] = useState(0);
const [resultHeight, setResultHeight] = useState(0);
useEffect(() => {
let stillMounted = true;
Image.getSize(uri, (realW, realH) => {
if (!paintedWidth || !stillMounted) return;
const shrinkRatio = realW / paintedWidth;
setResultHeight(realH / shrinkRatio);
});
return () => (stillMounted = false);
}, [paintedWidth]);
return (
<Image
style={[{ width: '100%' }, style, { height: resultHeight }]}
source={{ uri }}
onLayout={(event) => setPaintedWidth(event.nativeEvent.layout.width)}
/>
);
}
Upvotes: 1
Reputation: 1331
function ImageX( props){
const [state, setState] = React.useState({v:{height:1}});
useEffect(() => {
if(!props.source || !props.source.uri) return;
Image.prefetch(props.source.uri)
.then(() => {
Image.getSize(props.source.uri, (width, height) => {
let aspectRatio = height/width;
state.v.width=Dimensions.get('window').width;
state.v.height=aspectRatio*state.v.width;
setState({...state});
});
})
.catch(error => console.log(error))
},[props.source && props.source.uri]);
if (state.v.height <=1) //Image not in cache (disk) yet
{
return (
<Image {...props}/>
);
}
else
{
let neededst={v:{width:state.v.width, height:state.v.height}}
let st={v:props.style};
assignObject(neededst, st);
return (
<Image {...props} style={neededst.v}/>
);
}
}
function assignObject(target, source) {
if (!source) return;
for (let k in target) {
let v = target[k];
if (Object(v) === Object) assignObject(v, source[k]);
else {
if (source[k]) {
try {
Object.assign(v, source[k]);
} catch (e) {
alert(e);
}
}
}
}
for (let k in source) {
if (!target[k]) {
target[k] = source[k];
}
}
}
Using Image component from https://reactnativeelements.com/docs/image
Upvotes: -2
Reputation: 53
Let me share what I end up with, which allows to set correctly width or height by getting the image dimensions. In addition, the code allows to fetch a loading image while the large image data we need is being transfered:
Use static method Image.prefetch to have the image downloaded and available to cache.
Use static method Image.getSize to collect height and width and use it to compute an aspect ratio and then the final height (or width)
Display image with a default style to your prefered width (The height will be computed with aspect ratio kept)
function ImageX(props: {source: string, id: string})
{
const [imageHeight, setImageHeight] = React.useState(1);
Image.prefetch(props.source)
.then(() => {
Image.getSize(props.source, (width, height) => {
let aspectRatio = height/width;
setImageHeight(aspectRatio*Dimensions.get('window').width);
});
})
.catch(error => console.log(error))
if (imageHeight <=1) //Image not in cache (disk) yet
{
return (
<Image key={props.id} style={styleimg.image} source={{uri: 'http://www.dsdsd/loaderpreview.gif'}}/>
);
}
else
{
return (
<Image key={props.id} style={styleimg.image} height={imageHeight} source={{uri: props.source}}/>
);
}
}
const styleimg = StyleSheet.create({ image: { width: Dimensions.get('window').width, resizeMode: 'contain' //... // you can set a height defaults } });
Upvotes: 3
Reputation: 378
Right click on you image to get resolution. In my case 1233 x 882
const { width } = Dimensions.get('window');
const ratio = 882 / 1233;
const style = {
width,
height: width * ratio
}
<Image source={image} style={style} resizeMode="contain" />
That all
Upvotes: 7
Reputation: 85
this may help for auto adjusting the image height having image 100% width
image: { width: "100%", resizeMode: "center" "contain", height: undefined, aspectRatio: 1, }
Upvotes: 2
Reputation: 105
So the above answers are cute but they all have (imo) a big flaw: they are calculating the height of the image, based on the width of the user's device.
To have a truly responsive (i.e. width: 100%, height: auto
) implementation, what you really want to be doing, is calculating the height of the image, based on the width of the parent container.
Luckily for us, React Native provides us with a way to get the parent container width, thanks to the onLayout View method.
So all we need to do is create a View with width: "100%"
, then use onLayout
to get the width of that view (i.e. the container width), then use that container width to calculate the height of our image appropriately.
The below solution could be improved upon further, by using RN's Image.getSize, to grab the image dimensions within the ResponsiveImage component itself.
JavaScript:
// ResponsiveImage.ts
import React, { useMemo, useState } from "react";
import { Image, StyleSheet, View } from "react-native";
const ResponsiveImage = props => {
const [containerWidth, setContainerWidth] = useState(0);
const _onViewLayoutChange = event => {
const { width } = event.nativeEvent.layout;
setContainerWidth(width);
}
const imageStyles = useMemo(() => {
const ratio = containerWidth / props.srcWidth;
return {
width: containerWidth,
height: props.srcHeight * ratio
};
}, [containerWidth]);
return (
<View style={styles.container} onLayout={_onViewLayoutChange}>
<Image source={props.src} style={imageStyles} />
</View>
);
};
const styles = StyleSheet.create({
container: { width: "100%" }
});
export default ResponsiveImage;
// Example usage...
import ResponsiveImage from "../components/ResponsiveImage";
...
<ResponsiveImage
src={require("./images/your-image.jpg")}
srcWidth={910} // replace with your image width
srcHeight={628} // replace with your image height
/>
TypeScript:
// ResponsiveImage.ts
import React, { useMemo, useState } from "react";
import {
Image,
ImageSourcePropType,
LayoutChangeEvent,
StyleSheet,
View
} from "react-native";
interface ResponsiveImageProps {
src: ImageSourcePropType;
srcWidth: number;
srcHeight: number;
}
const ResponsiveImage: React.FC<ResponsiveImageProps> = props => {
const [containerWidth, setContainerWidth] = useState<number>(0);
const _onViewLayoutChange = (event: LayoutChangeEvent) => {
const { width } = event.nativeEvent.layout;
setContainerWidth(width);
}
const imageStyles = useMemo(() => {
const ratio = containerWidth / props.srcWidth;
return {
width: containerWidth,
height: props.srcHeight * ratio
};
}, [containerWidth]);
return (
<View style={styles.container} onLayout={_onViewLayoutChange}>
<Image source={props.src} style={imageStyles} />
</View>
);
};
const styles = StyleSheet.create({
container: { width: "100%" }
});
export default ResponsiveImage;
// Example usage...
import ResponsiveImage from "../components/ResponsiveImage";
...
<ResponsiveImage
src={require("./images/your-image.jpg")}
srcWidth={910} // replace with your image width
srcHeight={628} // replace with your image height
/>
Upvotes: 5
Reputation: 179
use aspectRatio property in style
Aspect ratio control the size of the undefined dimension of a node. Aspect ratio is a non-standard property only available in react native and not CSS.
docs: https://reactnative.dev/docs/layout-props#aspectratio
try like this:
import {Image, Dimensions} from 'react-native';
var width = Dimensions.get('window').width;
<Image
source={{
uri: '<IMAGE_URI>'
}}
style={{
width: width * .2, //its same to '20%' of device width
aspectRatio: 1, // <-- this
resizeMode: 'contain', //optional
}}
/>
Upvotes: 17
Reputation: 1565
I had problems with all above solutions. Finally i used aspectRatio to do the trick. When you know image width and height and they are large, just calculate aspectRatio and add it to image like:
<PhotoImage
source={{uri: `data:image/jpeg;base64,${image.base64}`}}
style={{ aspectRatio: image.width / image.height }}
/>
AspectRatio is a layout property of React Native, to keep aspect ratio of image and fit into parent container: https://facebook.github.io/react-native/docs/layout-props#aspectratio
Upvotes: 12
Reputation: 1242
"resizeMode"
isn't style property. Should move to Image component's Props
like below code.
const win = Dimensions.get('window');
const styles = StyleSheet.create({
image: {
flex: 1,
alignSelf: 'stretch',
width: win.width,
height: win.height,
}
});
...
<Image
style={styles.image}
resizeMode={'contain'} /* <= changed */
source={require('../../../images/collection-imag2.png')} />
...
Image's height won't become automatically because Image component is required both width and height in style props
. So you can calculate by using getSize() method for remote images like this answer and you can also calculate image ratio for static images like this answer.
There are a lot of useful open source libraries -
Upvotes: 58
Reputation: 4488
For image tag you can use this type of style, it worked for me:
imageStyle: {
width: Dimensions.get('window').width - 23,
resizeMode: "contain",
height: 211,
},
Upvotes: 3
Reputation: 1099
So after thinking for a while I was able to achieve height: auto in react-native image. You need to know the dimensions of your image for this hack to work. Just open your image in any image viewer and you will get the dimensions of the your image in file information. For reference the size of image I used is 541 x 362
First import Dimensions from react-native
import { Dimensions } from 'react-native';
then you have to get the dimensions of the window
const win = Dimensions.get('window');
Now calculate ratio as
const ratio = win.width/541; //541 is actual image width
now the add style to your image as
imageStyle: {
width: win.width,
height: 362 * ratio, //362 is actual height of image
}
Upvotes: 102
Reputation: 1890
You always have to set the width and height of an Image
. It is not going to automatically size things for you. The React Native docs says so.
You should measure the total height of the ScrollView
using onLayout
and set the height of the Image
s based on it. If you use resizeMode
of cover
it will keep the aspect ratio of your Image
s but it will obviously crop them if it's bigger than the container.
Upvotes: 6