如何设置图像宽度为100% 和高度自动在反应本地?

我试图在滚动视图中显示图像列表。宽度应该是100% ,而高度应该是自动的,保持长宽比。

我所做的搜索指向各种解决方案,提供全屏幕背景风格。

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>

我尝试了各种宽度组合: null、 height: null、 flex: 1、 alignSelf 等。除了高度不是动态的以外,上面的解决方案几乎可以正常工作。图像的某些部分不可见。

164216 次浏览

"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 -

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 Images based on it. If you use resizeMode of cover it will keep the aspect ratio of your Images but it will obviously crop them if it's bigger than the container.

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
}

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,
},

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

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.

  • On a node with a set width/height aspect ratio control 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

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
}}
/>

Solution April 2020:

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.

 

Just show me the code...

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
/>

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 image = () => (
<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.

this may help for auto adjusting the image height having image 100% width

image: { width: "100%", resizeMode: "center" "contain", height: undefined, aspectRatio: 1, }

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

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:

  1. Use static method Image.prefetch to have the image downloaded and available to cache.

  2. 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)

  3. 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 } });

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

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)}
/>
);
}

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"
/>
);
};