Автоматическая высота изображения изображения с помощью React Native

В моем приложении React Native я получаю изображения из API с неизвестными размерами. Как автоматически масштабировать высоту, если я знаю желаемую ширину?

Пример:

Я установил ширину в Dimensions.get('window').width. Как установить высоту и сохранить такое же соотношение?

export default class MyComponent extends Component {
  constructor(props) {
    super(props)
    this.state = {
      imgUrl: 'http://someimg.com/coolstuff.jpg'
    }
  }

  componentDidMount() {
    // sets the image url to state
    this.props.getImageFromAPi()
  }

  render() {
    return (
      <View>
        <Image 
          source={uri: this.state.imgUrl}
          style={styles.myImg}
        />
        <Text>Some description</Text>
      </View>
    )
  }
}

const styles = StyleSheet.create(
  myImg: {
    width: Dimensions.get('window').width,
    height: >>>???what goes here???<<<
  }
)

Ответы

Ответ 1

Попробуй это:

 import React, { Component, PropTypes } from "react";
 import { Image } from "react-native";

export default class ScaledImage extends Component {
constructor(props) {
    super(props);
    this.state = { source: { uri: this.props.uri } };
}

componentWillMount() {
    Image.getSize(this.props.uri, (width, height) => {
        if (this.props.width && !this.props.height) {
            this.setState({
                width: this.props.width,
                height: height * (this.props.width / width)
            });
        } else if (!this.props.width && this.props.height) {
            this.setState({
                width: width * (this.props.height / height),
                height: this.props.height
            });
        } else {
            this.setState({ width: width, height: height });
        }
    });
}

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

ScaledImage.propTypes = {
uri: PropTypes.string.isRequired,
width: PropTypes.number,
height: PropTypes.number
};

Я передаю URL как реквизит под названием uri. Вы можете указать свою width реквизита как Dimensions.get('window').width и это должно охватывать ее.

Обратите внимание, что это также будет работать, если вы знаете, какую вы хотите установить высоту, и вам нужно изменить ширину, чтобы сохранить соотношение. В этом случае вы указали бы height а не width.

Ответ 2

Существует свойство resizeMode, устанавливающее его на "содержать"

Пример:

<Image
    source={require('./local_path_to/your_image.png')}
    style={{ width: 30 }}
    resizeMode="contain"
 />

Источник: https://facebook.github.io/react-native/docs/image#resizemode

Ответ 3

Взгляните на эту библиотеку response-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} // height will be calculated automatically
       source={{uri: '<image uri>'}}
   />
);

Ответ 4

TypeScript- версия ответа @TheJizel с необязательным свойством style и обратным Image.getSize failure в Image.getSize:

import * as React from 'react'
import {Image} from 'react-native'

interface Props {
    uri: string
    width?: number
    height?: number
    style?
}

interface State {
    source: {}
    width: number
    height: number
}

export default class ScaledImage extends React.Component<Props, State> {
    constructor(props) {
        super(props)
        this.state = {
            source: {uri: this.props.uri},
            width: 0,
            height: 0,
        }
    }

    componentWillMount() {
        Image.getSize(this.props.uri, (width, height) => {
            if (this.props.width && !this.props.height) {
                this.setState({width: this.props.width, height: height * (this.props.width / width)})
            } else if (!this.props.width && this.props.height) {
                this.setState({width: width * (this.props.height / height), height: this.props.height})
            } else {
                this.setState({width: width, height: height})
            }
        }, (error) => {
            console.log("ScaledImage:componentWillMount:Image.getSize failed with error: ", error)
        })
    }

    render() {
        return <Image source={this.state.source} style={[this.props.style, {height: this.state.height, width: this.state.width}]}/>
    }
}

Пример использования:

<ScaledImage style={styles.scaledImage} uri={this.props.article.coverImageUrl} width={Dimensions.get('window').width}/>

Ответ 5

Сначала попробуйте это и посмотрите, работает ли оно для вас: https://github.com/facebook/react-native/commit/5850165795c54b8d5de7bef9f69f6fe6b1b4763d

Если это не так, вы можете реализовать свой собственный компонент изображения. Но вместо того, чтобы принимать ширину в качестве опоры, вы переопределяете метод onLayout, который дает желаемую ширину, чтобы вы могли рассчитать высоту. Это работает лучше, если вы не знаете ширины и хотите, чтобы RN сделал макет для вас. Недостаток onLayout вызывается после одного прохода макета и рендеринга. Таким образом, вы можете заметить, что ваши компоненты немного перемещаются.

Ответ 6

Вот сущность для довольно простого решения, которое использует предложение @Haitao Li для использования aspectRatio:

https://gist.github.com/tpraxl/02dc4bfcfa301340d26a0bf2140cd8b9

Никакой магии и никаких вычислений не требуется. Чистый "CSS", если вы знаете исходные размеры изображения.

Ответ 7

Предлагаемое решение работает, но вы должны загрузить изображение дважды, один раз, чтобы определить размер, и другой, чтобы фактически показать изображение, это другой подход, изображение загружается в квадрате вначале и изменяется в размере.

import React, { Component, } from "react";
import { Image } from "react-native";
import PropTypes from 'prop-types'

    export default class ScaledImage extends Component {
        state = {}

        componentWillMount() {
            const { uri, width, height } = this.props;
            this.setState({ source: { uri }, width: width || height, height: height || width });
        }

        render() {
            return (
                <Image
                    source={this.state.source}
                    onLoad={(value) => {
                        const { height, width } = value.nativeEvent.source;
                        if (this.props.width && !this.props.height) {
                            this.setState({
                                width: this.props.width,
                                height: height * (this.props.width / width)
                            });
                        } else if (!this.props.width && this.props.height) {
                            this.setState({
                                width: width * (this.props.height / height),
                                height: this.props.height
                            });
                        } else {
                            this.setState({ width: width, height: height });
                        }

                    }}
                    style={{ height: this.state.height, width: this.state.width }}
                />
            );
        }
    }

    ScaledImage.propTypes = {
        uri: PropTypes.string.isRequired,
        width: PropTypes.number,
        height: PropTypes.number
    };

Ответ 8

Основываясь на идее @TheJizel, я что-то приготовил, используя свойство стиля aspectRatio. Следующий класс работает, когда ширина установлена, но высота опущена. Это также работает с процентами как ширина.

import React from "react";
import { Image, ImageProps } from "react-native";

export default class ScaledImage extends React.Component<ImageProps> {

  state = {
    aspectRatio: 0
  }

  setAspectRatio(ratio:any) {
    this.setState({
      aspectRatio: ratio
    });
  }

  componentWillMount() {
    if (Array.isArray(this.props.source)) {
      console.warn("ScaledImage received an array as source instead of local file resource or ImageURISource.")
    } else if(typeof this.props.source === "number") {
      // Resolve local file resource
      const resolved = Image.resolveAssetSource(this.props.source);

      // We assume 100% width, so we set the aspect ratio we want for it height
      this.setAspectRatio(resolved.width / resolved.height);

    } else if (this.props.source.uri) {
      // Resolve remote resource
      Image.getSize(this.props.source.uri, (width, height) => {
         this.setAspectRatio( width / height);
      }, (err) => {
        console.error(err);
      });

    } else {
      console.warn("ScaledImage did not receive a valid source uri.");
    }
  }

  render() {
    if(!this.state.aspectRatio) return null;

    const props = {
      ...this.props,
      style: [this.props.style, {
        aspectRatio: this.state.aspectRatio
      }]
    };

    return (
      <Image {...props} />
    )
  }
}

Использование:

<ScaledImage source={{ uri: "<URI HERE>" }} style={{ width: "100%" }} />

Ответ 9

У вас есть 3 номера:

  1. ширина изображения
  2. высота изображения
  3. ширина экрана

и вы должны поставить "ширину экрана" в стиле ширины, а также рассчитать высоту для настройки в стиле?? !!

componentWillMount() {

    Image.getSize(this.props.product.image, (width, height) => {

        const screenWidth = Math.round(Dimensions.get('window').width);  
        this.setState({screenWidth:screenWidth});
        Calculatedheight = screenWidth * height / width ;
        this.setState({Calculatedheight : Calculatedheight });

    });

}

а также

<Image
  source={{uri: product.image,cache: 'only-if-cached'}}
  style={{ height: this.state.screenHeight , width: this.state.Calculatedheight }}

/>