如何在React Native中将图像宽度设置为100%,高度自动调整?

113

我正在尝试在滚动视图中显示图像列表。宽度应为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、高度:null、flex:1、alignSelf等。上述解决方案几乎可以使用,但是高度不是动态的,图像的某些部分无法显示。


请看一下react-native-scalable-image - Ihor Burlachenko
14个回答

97

思考了一会儿后,我成功地在 React Native 的图像中实现了 height: auto。

要使用这个小技巧,您需要知道图像的尺寸。只需在任何图像查看器中打开您的图像,即可获取文件信息中您的图像的尺寸。参考所使用的图像大小为 541 x 362。

首先从 react-native 中导入尺寸 (Dimensions)。

import { Dimensions } from 'react-native';

那么你需要获取窗口的尺寸。

const win = Dimensions.get('window');

现在按照以下方式计算比率:

const ratio = win.width/541; //541 is actual image width

现在为您的图像添加样式:

imageStyle: {
    width: win.width,
    height: 362 * ratio, //362 is actual height of image
}

3
你很聪明,我还是无法搞清楚这个问题。但它确实可以工作 :) - Kirankumar Dafda
2
如此聪明的解决方案。 - Adam
16
截至2020年3月,这仍然是最佳解决方案吗? - Bataleon
2
是的!这个是唯一一个在所有情况下都有效的方法。唯一需要考虑的是,如果图像尺寸是静态资源,那么你可以很容易地解决它们。只需执行以下操作: import myImage from '../../assets/myimage.png'; const { width, height } = Image.resolveAssetSource(myImage) - webdevbyjoss
2
这是正确的吗?图像的宽度是窗口宽度,而不是100%。 - Marios G
显示剩余4条评论

56

"resizeMode" 不是一个样式属性,应该像下面的代码一样将其移到 Image 组件的 Props 中。

Translated content:

"resizeMode" 不是一个样式属性,应该移动到 Image 组件的 Props 中,如下所示。

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')} /> 
...

由于组件在style props中需要宽度和高度,因此图像的高度不会自动调整。对于远程图像,您可以使用getSize()方法进行计算,如此答案所示;对于静态图像,您也可以计算出图像比例,例如此答案所示。

有许多有用的开源库 -


6
根据https://facebook.github.io/react-native/docs/image.html,在图片上有一个样式属性。将其更改为“property”也没有起作用。 - Prabhakar Bhat
7
这并没有回答关于如何自动计算高度的问题。 - chrmod
这个可以运行,但你应该说需要“从'react-native'导入{ Dimensions }”。 - Sabba Keynejad
3
除了上面的评论所说的内容之外," getSize() "不能用于静态图片资源。 - Betty
@Betty 我认为getSize()对于静态图片是有效的。 - kojow7

53
我找到了一个解决方案,适用于具有已知图像纵横比(宽度/高度)的情况下,宽度:"100%",高度:"自动"
以下是代码:
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
    }
});

这是我能找到的最简单的方法,而不使用onLayoutDimension计算。如果需要的话,甚至可以将其包装在一个简单的可重用组件中。如果有人正在寻找一个简单的实现,请试一试。

1
这绝对是正确的机制。来自react-native文档的描述是:"在具有设置flex基础的节点上,如果未设置,则aspectRatio控制节点在交叉轴上的大小"。如果父元素是行,则aspectRatio将设置高度,但如果它是列,则会影响宽度。 - cdosborn
1
你正式成为我的英雄。 - mdmb

16

在样式中使用 aspectRatio 属性。

Aspect ratio 控制节点未定义的尺寸。Aspect ratio 是一个非标准属性,仅在 React Native 中可用,而不是 CSS。

  • 对于设置了宽度/高度的节点,aspect ratio 控制未设置尺寸的维度大小
  • 对于设置了 flex 基础值的节点,如果未设置,则 aspect ratio 控制节点在交叉轴上的大小
  • 对于具有测量函数的节点,aspect ratio 的作用类似于测量函数测量 flex 基础值
  • 对于具有 flex grow/shrink 的节点,如果未设置,则 aspect ratio 控制节点在交叉轴上的大小
  • aspect ratio 考虑最小/最大尺寸

文档: https://reactnative.dev/docs/layout-props#aspectratio

试试这样做:

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

11

我遇到了所有上述解决方案的问题。最终,我使用了aspectRatio来解决问题。当您知道图像的宽度和高度并且它们很大时,只需计算宽高比并将其添加到图像中即可:

<PhotoImage
    source={{uri: `data:image/jpeg;base64,${image.base64}`}}
    style={{ aspectRatio: image.width / image.height }}
 />

AspectRatio 是 React Native 的一个布局属性,用于保持图像的纵横比并适应父容器:https://facebook.github.io/react-native/docs/layout-props#aspectratio


2
这是唯一真正的答案。进一步扩展,如果您正在从网络请求图像,则可以使用react-native中的Image.getSize静态方法获取其大小。 - sospedra

7

右击图像以获取分辨率。在我的情况下为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


6
你必须始终设置Image的宽度和高度,它不会自动为你调整大小。React Native文档如此说明
你应该使用onLayout测量ScrollView的总高度,并根据它设置Image的高度。如果你使用resizeModecover,它会保持Image的纵横比,但如果它比容器大,它显然会裁剪它们。

9
仅使用自动调整大小无法适用于远程图像。 OP所提到的是本地静态图像文件,在React Native中可以自动调整大小,因为它们的尺寸在挂载时立即可用。 - PowerOfM

5

2020年4月的解决方案:

上面提供的答案都很好,但它们都有一个(在我看来)很大的缺陷:它们是根据用户设备的宽度计算图像的高度。

要实现真正响应式(即width: 100%, height: auto)的实现,您需要根据父容器的宽度计算图像的高度。

幸运的是,React Native提供了一种方法来获取父容器的宽度,这得益于onLayout View method

因此,我们所需要做的就是创建一个具有width: "100%"属性的View,然后使用onLayout方法获取该视图的宽度(即容器宽度),然后使用该容器宽度适当地计算图像的高度。

 

直接展示代码。。。

通过在ResponsiveImage组件本身内部使用RN的Image.getSize方法,以下解决方案可以进一步改进。

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

2
正如您所建议的那样,https://github.com/vivaxy/react-native-auto-height-image 对于这个问题做得非常好。 - Ajay

3

让我分享一下我的经验,可以通过获取图像的尺寸来正确设置宽度或高度。此外,该代码还允许在我们需要传输大图像数据时获取加载图像:

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


3
标签可以使用以下样式,这个对我很有效:
imageStyle: {
    width: Dimensions.get('window').width - 23,
    resizeMode: "contain",
    height: 211,
 },

resizeMode 不能在样式中。 - Kaushil Ruparelia
1
@KaushilRuparelia,这绝对是可以的。 - Arafat Zahan
很高兴知道这个。 - Kaushil Ruparelia

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接