【问题标题】:Maintain aspect ratio of image with full width in React Native在 React Native 中保持全宽图像的纵横比
【发布时间】:2023-03-21 15:05:01
【问题描述】:

我有一个关于标签的查询。我希望图像采用 alignSelf:stretch 的整个宽度,但我也希望高度根据图像的纵横比。我怎样才能实现这样的目标?

所以我想要一种方法将高度指定为图像宽度的比率。

【问题讨论】:

  • 这里还是没有好的答案!
  • 发现这个repo做得很好。

标签: react-native


【解决方案1】:

对于宽高比为 3:2 的水平图像,请使用 style={{ aspectRatio: 3/2 }}

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

(适用于 RN 0.40+)

【讨论】:

  • 美丽。我与flex: 1 一起执行此操作,并将容器设置为flexDirection: 'row',并且我能够让我的图像在保留纵横比的情况下拉伸包含元素的宽度 100%。谢谢。
【解决方案2】:
<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,
  },

});

【讨论】:

  • 这个答案打开了我的思路,解决了我的问题。但是,出现了一个问题,为什么height 必须是undefined 才能工作?如果我们使用,例如height: X,省略width,它似乎不需要width: undefined
  • height:undefined 是最保守的秘密。我的意思不是很好。
  • 感谢'aspectRatio' 的提示,但我不需要'height: undefined' 才能工作 - 也许更新已经解决了这个问题?
  • 想知道这是否是 React Native 的错误?
  • 仅供参考,这不适用于 react-native-web
【解决方案3】:

我喜欢 bdv 的方法,并且我在我的应用程序中几乎所有地方都使用这种图像。这就是为什么我创建了一个自己的组件,它使用onLayout 来支持设备旋转。

import resolveAssetSource from 'resolveAssetSource';
import React, {Component} from 'react';
import {Image, View} from 'react-native';

export default class FullWidthImage extends Component {
    constructor() {
        super();

        this.state = {
            width: 0,
            height: 0
        };
    }

    _onLayout(event) {
        const containerWidth = event.nativeEvent.layout.width;

        if (this.props.ratio) {
            this.setState({
                width: containerWidth,
                height: containerWidth * this.props.ratio
            });
        } else if (typeof this.props.source === 'number') {
            const source = resolveAssetSource(this.props.source);

            this.setState({
                width: containerWidth,
                height: containerWidth * source.height / source.width
            });
        } else if (typeof this.props.source === 'object') {
            Image.getSize(this.props.source.uri, (width, height) => {
                this.setState({
                    width: containerWidth,
                    height: containerWidth * height / width
                });
            });
        }
    }

    render() {
        return (
            <View onLayout={this._onLayout.bind(this)}>
                <Image
                    source={this.props.source}
                    style={{
                        width: this.state.width,
                        height: this.state.height
                    }} />
            </View>
        );
    }
}

你可以这样使用它:

<FullWidthImage source={{uri: 'http://example.com/image.jpg'}} />
<FullWidthImage source={require('./images/image.jpg')} />

或者如果你知道这样的比率:

<FullWidthImage source={{uri: 'http://example.com/image.jpg'}} ratio={0.5} />
<FullWidthImage source={require('./images/image.jpg')} ratio={0.5} />

【讨论】:

  • 感谢您轻松复制/粘贴组件?
  • 如果Image的来源需要是静态图片资源,使用require,比如require('../../some.png')
  • 几个月以来我一直在使用这种方法。这很好用,但会减慢图像的渲染速度。使用这种方法的图像渲染比使用硬编码的宽度-高度的图像渲染花费更多的时间(几乎是两倍)(在 2G 网络/慢速网络中测试以获得更明显的差异)。需要最佳解决方案。
  • @KalpeshWadekar 我已经为我的组件添加了一个ratio 属性。这应该可以解决您的问题。
  • @Halt 你可以使用 Image.resolveAssetSource(this.props.source)。参考:stackoverflow.com/questions/41997611/…
【解决方案4】:

其实很简单。

Image 类有一个getSize 方法。 [1]

假设您为aspectRatioImage 创建了一个组件,并且每次componentWillMount 触发时都会计算适当的值。

那么您的代码将如下所示:

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

现在图像的高度和宽度已经保存在组件的状态中,你可以运行

 <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

【讨论】:

  • 我编辑了你的帖子,这样我就可以删除我的反对票,我这样做了。我认为图像尺寸的状态不正确(仍然不确定),但看到文档建议了这种方法。
  • @MattParrilla 这在一定程度上取决于您将使用它的目的。我可以想象你想用它来“缩放”图像,直到它到达屏幕上某个地方的边界。在这种情况下,状态会很好。如果要显示静态图像,那么我认为将其作为道具传递会更有意义。
  • 如果图像仍在屏幕上,此方法效果很好。我遇到了图像 getSize 仍在处理中的问题,用户切换到其他屏幕,setState 现在设置为未安装组件的值。我希望 Image 组件本身有一种更简洁的方法来根据其父级的宽度来缩放高度。上面的解决方案非常适合修复图像位置。当我们在 FlatList 中实现动态调整图像大小时,它会导致很多警告,其中组件卸载任何不在屏幕上的项目以减少内存占用
  • 唯一有效的答案
【解决方案5】:

您可以使用react-native-scalable-image。以下示例将完成这项工作:

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

【讨论】:

    【解决方案6】:

    您可以根据宽高比计算图像高度。

    所以如果图片原本是200x100,在设置resizeMode为stretch后:

    var deviceWidth: Dimensions.get('window').width;
    
    ...
    
    myImage {
        width: deviceWidth,
        height: deviceWidth * 0.5
    }
    

    我知道这可能不是最佳做法,但它对我需要与其他图像保持某种关系等各种尺寸的图像有很大帮助。

    【讨论】:

      【解决方案7】:

      通常,执行以下操作会根据方向将图像渲染为最大宽度/高度,同时保持图像本身的纵横比:

      render(){
          return(<Image style={{'width': Dimensions.get('window').width, 
                               'height': Dimensions.get('window').height}}
                        resizeMode='contain'
                        source='[URL here]'
                 </Image>);
      }
      

      使用 'contain' 和 resizeMode:统一缩放图像(保持图像的纵横比),使图像的两个尺寸(宽度和高度)都等于或小于视图的相应尺寸(减去填充) .

      更新:* 不幸的是,resizeMode 的“包含”似乎存在一个常见错误,特别是在为 Android 使用 react native 时:https://github.com/facebook/react-native/pull/5738*

      【讨论】:

      • 这会在图像上方和下方产生大量空白? (在 iOS 上)
      • 上面提出的解决方案只是试图将图像适合整个屏幕,这不是问题的全部。问题与如何将与原始图像具有相同纵横比的图像调整为给定宽度
      【解决方案8】:

      在我的情况下,我还必须将高度设置为“自动”:

      {
          width: 200,
          height: 'auto',
          aspectRatio: 16 / 9,
      }
      

      【讨论】:

        【解决方案9】:

        就我而言,我在我的 React Native (v0.62+) 项目中使用 Styled Components

        我需要为具有已定义 width 和未定义 heightImage 组件指定一个正方形 aspectRatio

        我发现样式height:0; 实现了我想要的“方形图像”结果:

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

        此方法也适用于全宽图像样式 - 将 width:50% 替换为 width:100% 会产生具有正确纵横比的每个图像的预期结果。

        【讨论】:

          【解决方案10】:
          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>
              )
          

          }

          【讨论】:

          • 像魅力一样工作!并引用docs“为了检索图像尺寸,可能首先需要加载或下载图像,然后将其缓存。”
          【解决方案11】:

          我尝试了 Image.getSize 方法,但遇到了问题,因为我们将所有图像链接收集在一个配置文件中,然后将 ImageURISource 传递给 Image 的 source prop。

          我的解决方案是等待 Image onLayout 回调来获取它的布局属性并使用它来更新尺寸。我为此创建了一个组件:

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

          这将更新图像的尺寸以匹配屏幕的宽度。

          【讨论】:

            【解决方案12】:

            使用resizeMode='contain'flex=1,我可以在保持纵横比的同时获得完整的图像。

              <Image
                 source={{ uri: 'URI' }}
                 resizeMode="contain"
                 style={{flex:1}} />
            

            图片需要在容器 View 中定义了 flex 或 height ,这样图片的 flex 才能起作用。

            【讨论】:

              【解决方案13】:

              使用resizeMode='contain'

              <Image style={{height:'100%', width:'100%'}} resizeMode="contain" source={{uri:this.state.imgSource}} />
              

              这将保持原来的纵横比,给定的宽度和高度为 max-height 和 max-width。

              【讨论】:

                【解决方案14】:

                你可以这样使用:

                <View style={{height:200}} >
                <Image source={require('image!MyImage') style={{ resizeMode:Image.resizeMode.ratio, flex:1 }}} />
                </View>
                

                请注意,您仍然必须在视图容器上设置高度。

                【讨论】:

                猜你喜欢
                • 2017-08-19
                • 2018-04-26
                • 1970-01-01
                • 1970-01-01
                • 1970-01-01
                • 1970-01-01
                • 1970-01-01
                • 2016-12-20
                • 2017-04-11
                相关资源
                最近更新 更多