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