Компонент для автоматического маштабирования высоты картинки в ReactNative, относительно ее ширины

Cover Image for Компонент для автоматического маштабирования высоты картинки в ReactNative, относительно ее ширины

В React Native, когда вам нужно создать макет для разных мобильных устройств, обычно вы используете относительные единицы измерения для установки ширины изображений. Однако высота не вычисляется автоматически, что может привести к проблемам с макетом.

Для того чтобы высота контейнера изображения сохраняла пропорции самого изображения, мы можем использовать подход из веб-разработки.

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

Для вычисления процентного соотношения сторон исходного изображения мы можем использовать следующую формулу:

const aspectRatio = height / (width / 100);

​ Где height - высота исходного изображения, а width - его ширина.

Значение aspectRatio мы будем использовать для установки paddingTop нашего контейнера. Таким образом, контейнер будет иметь те же пропорции высоты и ширины, что и исходное изображение, при этом значения ширины и высоты будут указаны в процентах, что позволяет встраивать этот контейнер в любой макет.

Для размещения изображения внутри этого контейнера мы можем использовать следующий подход:

<View style={{ position: 'relative', paddingTop: `${aspectRatio}%` }}>
  <View style={{ position: 'absolute', left: 0, right: 0, top: 0, bottom: 0 }}>
    <Image source={imageSource} style={{ width: '100%', height: '100%' }} />
  </View>
</View>

Здесь aspectRatio - это значение, которое мы рассчитали ранее. imageSource - это источник изображения.

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

Вот полный код компонента:

import React from 'react';
import {Image, ImageProps, ImageResizeMode, StyleSheet, View, ViewProps} from 'react-native';

interface ScaleImageProps {
  /**
   * if you don’t need the image width as a percentage and automatic height scaling,
   * then perhaps you need to use component Image
   */
  containerWidth: `${number}%` | number;
  imageWidth: number;
  imageHeight: number;
  source: ImageProps['source'];
  containerStyle?: ViewProps['style'];
  resizeMode: ImageResizeMode;
}

export const ScaleImage: React.FC<ScaleImageProps> = ({
  containerWidth,
  imageWidth,
  imageHeight,
  source,
  containerStyle,
  resizeMode
}) => {
  const paddingTop: `${number}%` = `${imageHeight / (imageWidth / 100)}%`;

  return (
    <View
      style={[
        {
          width: containerWidth,
          paddingTop,
        },
        containerStyle,
      ]}>
      <View style={styles.wrapper}>
        <Image style={styles.image} resizeMode={resizeMode} source={source} />
      </View>
    </View>
  );
};

const styles = StyleSheet.create({
  wrapper: {
    position: 'absolute',
    left: 0,
    right: 0,
    top: 0,
    bottom: 0,
  },
  image: {
    width: '100%',
    height: '100%',
  },
});