Auto scale image height with React Native

后端 未结 14 1176
陌清茗
陌清茗 2020-12-04 23:19

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

相关标签:
14条回答
  • 2020-12-05 00:03

    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

    0 讨论(0)
  • 2020-12-05 00:06

    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}/>
    
    0 讨论(0)
  • 2020-12-05 00:06

    one solution out of many

    <Image source={...} style={{ transform: [{ scale: 0.5 }] }} />
    
    0 讨论(0)
  • 2020-12-05 00:07

    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
        };
    
    0 讨论(0)
  • 2020-12-05 00:08

    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} />
    
    0 讨论(0)
  • 2020-12-05 00:11

    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

    0 讨论(0)
提交回复
热议问题