使用React Native自动缩放图像高度

时间:2017-02-10 23:06:43

标签: 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???<<<
  }
)

16 个答案:

答案 0 :(得分:32)

试试这个:

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

我将网址作为名为uri的道具传递。您可以将width道具指定为Dimensions.get('window').width,这应该涵盖它。

请注意,如果您知道要将高度设置为什么,并且需要调整宽度以保持比率,这也会起作用。在这种情况下,您可以指定height道具而不是width道具。

答案 1 :(得分:8)

有一个属性 resizeMode 设置为“包含”

示例:

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

来源:https://facebook.github.io/react-native/docs/image#resizemode

答案 2 :(得分:3)

看看这个图书馆react-native-scalable-image。它完全符合您的要求。

counter++;
label1.Text = "number " + counter.ToString();

答案 3 :(得分:2)

TypeScript 版本的@TheJizel回答,其中包含style中的可选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}/>

答案 4 :(得分:2)

@ TheJizel 的挂钩版本。我知道宽度,但想要图像的高度,因此以下内容对我有用:

    const ScaledImage = props => {

    const [width, setWidth] = useState()
    const [height, setHeight] = useState()
    const [imageLoading, setImageLoading] = useState(true)

    useEffect(() => {
        Image.getSize(props.uri, (width1, height1) => {
            if (props.width && !props.height) {
                setWidth(props.width)
                setHeight(height1 * (props.width / width1))
            } else if (!props.width && props.height) {
                setWidth(width1 * (props.height / height1))
                setHeight(props.height)
            } else {
                setWidth(width1)
                setHeight(height1)
            }
            setImageLoading(false)
        }, (error) => {
            console.log("ScaledImage,Image.getSize failed with error: ", error)
        })
    }, [])


    return (
        height ?
            <View style={{ height: height, width: width, borderRadius: 5, backgroundColor: "lightgray" }}>
                <Image
                    source={{ uri: props.uri }}
                    style={{ height: height, width: width, borderRadius: 5, }}
                />
            </View>
            : imageLoading ?
                <ActivityIndicator size="large" />
                : null
    );
}

用法:

<ScaledImage width={Dimensions.get('window').width * 0.8} uri={imageurl} />

答案 5 :(得分:1)

首先尝试一下,看看它是否适合您:https://github.com/facebook/react-native/commit/5850165795c54b8d5de7bef9f69f6fe6b1b4763d

如果没有,那么您可以实现自己的图像组件。但是,不是将宽度作为道具,而是覆盖onLayout方法,该方法为您提供所需的宽度,以便您可以计算高度。如果您不知道宽度并希望RN为您进行布局,则效果会更好。缺点是在布局和渲染一次通过后调用onLayout。所以你可能会注意到你的组件移动了一点。

答案 6 :(得分:1)

以下是一个非常简单的解决方案的要点,它利用@Haitao Li的建议来使用aspectRatio:

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

没有必要的魔法和计算。 Pure&#34; CSS&#34;如果你知道原始图像的尺寸。

答案 7 :(得分:1)

以下代码对我有用:

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={{
             width: Dimensions.get('window').width,
             height: Dimensions.get('window').width*1.28
          }}
        />
        <Text>Some description</Text>
      </View>
    )
  }
}

答案 8 :(得分:1)

这个人在世博会上为我工作

<Image style={{flex:1,width:null,height:null }} resizeMode={'contain'}  source={{uri: 'http://134.209.40.60/meApunto/1567655610795_1944474896.png'}}></Image>

https://forums.expo.io/t/how-to-fit-a-big-image-into-a-fixed-container-without-resizemode-help/27639

答案 9 :(得分:0)

建议的解决方案有效,但是您必须下载图像两次,一次是确定大小,另一次是实际显示图像,这是另一种方法,图像首先加载平方并调整大小。

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

答案 10 :(得分:0)

基于@TheJizel的想法,我使用AspectRatio样式属性制作了一些东西。设置宽度后,以下类将起作用,但是高度将被省略。这也适用于宽度百分比。

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

export default class ScaledImage extends React.Component {

  state = {
    aspectRatio: 0
  }

  setAspectRatio(ratio) {
    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's 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%" }} />

答案 11 :(得分:0)

您有3个数字:

  1. 图像宽度
  2. 图像高度
  3. 屏幕宽度

,您应该在宽度样式中放置“屏幕宽度”,并计算 设置样式的高度?? !!

print(get)

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

    });

}

答案 12 :(得分:0)

这是我在生产中使用的一些代码。后端用户可以制作任何大小和宽高比的徽标图像,但是我需要徽标适合最大宽度的确切高度。我的自扩展组件是什么导致的:

import React, { useState, useLayoutEffect, SFC } from "react";
import { Image } from "react-native";
import { Spinner } from "native-base";


interface INetworkImage {
    targetHeight: number,
    uri: string,
    maxWidth: number
}

const NetworkImage: SFC<INetworkImage> = ({ uri, targetHeight, maxWidth }) => {

    useLayoutEffect(() => setNaturalDimensions(uri), []);

    const [imageWidth, setWidth] = useState(0);
    const [imageHeight, setHeight] = useState(0);
    const [scaleFactor, setScale] = useState(1);

    function setNaturalDimensions(uri: string) {
        Image.getSize(uri, (width: number, height: number) => {
            if (width > maxWidth) {
                // too wide case
                setScale(maxWidth / width);
            } else {
                // scale to height case
                setScale(targetHeight / height);
            }
            setWidth(width);
            setHeight(height);
        }, (error: any) => {
            console.log("error", error);
        });
    }
    function adjustView(e) {
        if (e.nativeEvent.layout.width > maxWidth) {
            setScale(scaleFactor * (maxWidth/e.nativeEvent.layout.width));
        }
    }
    return (
        imageHeight ?
        <Image
            onLayout={(e) => adjustView(e)}
            source={{ uri: uri }}
            style={{
                width: imageWidth * scaleFactor,
                height: imageHeight * scaleFactor,
                resizeMode: "contain",
            }}
        />:
        <Spinner color='#454c7a' />
        );
}
export default NetworkImage;

然后我通过将uri,targetHeight和maxwidth用作道具来使用它:

export const deviceWidth = Dimensions.get("window").width;

<NetworkImage
    uri={"https://purdyPic.com/image1"}
    targetHeight={300}
    maxWidth={deviceWidth * 0.85}
                          />

答案 13 :(得分:0)

众多解决方案中的一种

<Image source={...} style={{ transform: [{ scale: 0.5 }] }} />

答案 14 :(得分:0)

所以这一切都帮了我很多 ?

我的特定场景涉及从服务器获取纵向或横向图像,我需要将它们放入 <View>

这意味着“已知”尺寸是该视图的,我通过 onLayout 获得(简化代码仅显示设置“高度”的示例):

<View onLayout={(event) => setCellHeight(event.nativeEvent.layout.height)}>

现在使用已知的 displayAreaHeightdisplayAreaWidth 值,我需要调整图像大小:

  // Set image size for portrait/landscape scenarios, reducing the total image size when
  // an overflow of the display area would occur.

  if (image.height > image.width) { // Portrait Image
    const ratio = displayAreaHeight / image.height;
    imageHeight = displayAreaHeight;
    imageWidth = image.width * ratio;
    if (imageWidth > displayAreaWidth) {
      const heightReductionRatio = displayAreaWidth / imageWidth;
      imageHeight *= heightReductionRatio;
      imageWidth = displayAreaWidth;
    }
  } else {
    const ratio = displayAreaWidth / image.width;
    imageHeight = image.height * ratio;
    imageWidth = displayAreaWidth;
    if (imageHeight > displayAreaHeight) {
      const widthReductionRatio = displayAreaHeight / imageHeight;
      imageWidth *= widthReductionRatio;
      imageHeight = displayAreaHeight;
    }
  }

希望这个,连同这里的所有其他伟大的反应,帮助别人?

答案 15 :(得分:0)

基于以上答案,我制作了以下功能组件:

    import { useFocusEffect } from '@react-navigation/native';
    import React from 'react';
    import { ImageProps, ImageURISource } from 'react-native';
    import { useIsMounted } from '../../hooks/is-mounted';
    import { DrImageStyl } from './styled';
    import { getImageSizes } from '../../utils/util';
    
    interface DrSource extends ImageURISource {
      uri: string;
    }
    
    interface DrImageProps extends ImageProps {
      source: DrSource;
      width?: number;
      height?: number;
    }
    
    const DrImage: React.FC<DrImageProps> = ({
      width: widthProp,
      height: heightProp,
      source,
      ...rest
    }: DrImageProps) => {
      const isMountedRef = useIsMounted();
    
      const [sizes, setSizes] = React.useState({
        width: widthProp,
        height: heightProp,
      });
    
      useFocusEffect(
        React.useCallback(() => {
          const getImageSizesState = async () => {
            try {
              const { width, height } = await getImageSizes({
                uri: source.uri,
                width: widthProp,
                height: heightProp,
              });
    
              if (isMountedRef.current) {
                setSizes({ width, height });
              }
            } catch (error) {
              console.log('Erro em dr-image getImageSizesState:', error);
            }
          };
    
          getImageSizesState();
        }, [widthProp, heightProp, source.uri])
      );
    
      return (
        <>
          {!!sizes.height && !!sizes.width && (
            <DrImageStyl sizes={sizes} source={source} {...rest} />
          )}
        </>
      );
    };

export default DrImage;

useIsMounted:

import React from 'react';

export const useIsMounted = (): React.MutableRefObject<boolean> => {
  const isMountedRef = React.useRef(false);
  React.useEffect(() => {
    isMountedRef.current = true;
    return () => {
      isMountedRef.current = false;
    };
  }, []);
  return isMountedRef;
};

DrImageStyl:

import React from 'react';
import styled, { css } from 'styled-components/native';

interface Sizes {
  width?: number;
  height?: number;
}

interface DrImageStylProps {
  sizes: Sizes;
}

export const DrImageStyl = styled.Image<DrImageStylProps>`
  ${({ sizes }) => {
    const { width, height } = sizes;

    return css`
      ${width ? `width: ${width}px;` : ''}
      ${height ? `height: ${height}px;` : ''}
    `;
  }}
`;

getImageSizes:

import { Image } from 'react-native';

interface GetImageSizesParams {
  uri: string;
  height?: number;
  width?: number;
}

export function getImageSizes({
  height: heightParam,
  width: widthParam,
  uri,
}: GetImageSizesParams): Promise<{
  width: number;
  height: number;
}> {
  return new Promise((resolve, reject) => {
    function onSuccess(width: number, height: number) {
      let widthResolve: number | undefined;
      let heightResolve: number | undefined;

      if (widthParam && !heightParam) {
        widthResolve = widthParam;
        heightResolve = height * (widthParam / width);
      } else if (!widthParam && heightParam) {
        widthResolve = width * (heightParam / height);
        heightResolve = heightParam;
      } else {
        widthResolve = widthParam;
        heightResolve = heightParam;
      }

      resolve({
        width: widthResolve as number,
        height: heightResolve as number,
      });
    }

    function onError(error: any) {
      reject(error);
    }
    try {
      Image.getSize(uri, onSuccess, onError);
    } catch (error) {
      console.log('error', error);
    }
  });
}