In my React Native app, I am fetching images from an API with unknown dimensions. How do I auto scale the height if I know my desired width?
Example:
I set t
I built a small component around that problem. It uses react-native-auto-height-image library. The advantage is that you can either use it as a Image or a ImageBackground, if you provide any children.
npm i react-native-auto-height-image
import React, { useState, ReactNode } from "react";
import { ViewProps, View, LayoutChangeEvent, StyleSheet } from "react-native";
import AutoHeightImage, {
AutoHeightImageProps,
} from "react-native-auto-height-image";
interface WrappedAutoHeightImageProps
extends Omit<AutoHeightImageProps, "width"> {
children?: ReactNode;
containerProps?: ViewProps;
}
/**
* Uses wrapper to set dynamic width of a AutoHeightImage
* @see https://github.com/vivaxy/react-native-auto-height-image/issues/9
*/
export const WrappedAutoHeightImage = ({
children,
containerProps,
...rest
}: WrappedAutoHeightImageProps) => {
const [wrapperWidth, setWrapperWidth] = useState(0);
const [imageHeight, setImageHeight] = useState(0);
const onWrapperLayout = (event: LayoutChangeEvent) =>
setWrapperWidth(event.nativeEvent.layout.width);
const onImageLayout = (event: LayoutChangeEvent) =>
setImageHeight(event.nativeEvent.layout.height);
return (
<View
onLayout={onWrapperLayout}
{...containerProps}
style={[
{
alignItems: "center",
display: "flex",
flexDirection: "row",
height: imageHeight,
justifyContent: "center",
width: "100%",
},
containerProps?.style,
]}
>
<AutoHeightImage
onLayout={onImageLayout}
onError={(error) => console.log("error", error)}
width={wrapperWidth}
style={[StyleSheet.absoluteFill]}
{...rest}
/>
{imageHeight ? children : undefined}
</View>
);
};
Usage
// As image
<WrappedAutoHeightImage source={{ uri: "https://cdn.pixabay.com/photo/2020/01/26/19/32/architecture-4795667_960_720.jpg" }} />
// As image background
<WrappedAutoHeightImage
source={{ uri: "https://cdn.pixabay.com/photo/2018/09/24/03/05/cat-3699032_960_720.jpg" }}
>
<Text>This is centered vertically and horizontally per default and has an image as background.</Text>
</WrappedAutoHeightImage>
You can pass props to the container via containerProps, all other props are spread to the image. Also feel free to change my provided styles.
Read more about the library here
TypeScript version of @TheJizel answer with optional style property and failure callback in 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}]}/>
}
}
Example usage:
<ScaledImage style={styles.scaledImage} uri={this.props.article.coverImageUrl} width={Dimensions.get('window').width}/>
one solution out of many
<Image source={...} style={{ transform: [{ scale: 0.5 }] }} />
The proposed solution works, but you have to download image twice, once to determine the size and another to actually show the image, this is a different approach, image is loaded squared initially and resized.
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
};
Hooks version of @TheJizel answer. I knew the width but wanted the height of the image, so the below worked for me :
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
);
}
Usage :
<ScaledImage width={Dimensions.get('window').width * 0.8} uri={imageurl} />
There is a property resizeMode set it to 'contain'
Example:
<Image
source={require('./local_path_to/your_image.png')}
style={{ width: 30 }}
resizeMode="contain"
/>
Source: https://facebook.github.io/react-native/docs/image#resizemode