最近使用 RN 做图片显示的功能,需要图片固定宽度,然后按比例显示,本来以为设置了 width
和 resizeMode
就可以,实际测试并不是如此,要么就是显示不全,要么就是宽度达不到要求;实际发现,获取图片的比例,然后计算出高度,才是可行的做法
功能实现
react native 的 source 参数支持传入三种类型,但实际上 ImageURISource[]
基本没看到有这种用法,也没看到官网有对应的介绍,实际测试发现传数组确实可以显示图片,但却只会显示第一张图片,为了安全,我们仍做兼容
本地图片和网络图片获取宽高用的是不同的 api,本地用的是 Image.resolveAssetSource
,网络用的是 Image.getSize
,因此需要判断传入的 source 是哪种类型,是本地图片还是网络图片,搭配 typescript 的 is 关键字,可以写出
import { ImageRequireSource, ImageSourcePropType, ImageURISource } from 'react-native';
const isURISourceArray = (source: ImageSourcePropType): source is ImageURISource[] => {
return Array.isArray(source);
};
const isURISource = (source: ImageSourcePropType): source is ImageURISource => {
return !Array.isArray(source) && typeof source !== 'number';
};
const isRequireSource = (source: ImageSourcePropType): source is ImageRequireSource => {
return typeof source === 'number';
};
以下是最终的代码
import { useEffect, useState } from 'react';
import { Image, ImageProps, ImageRequireSource, ImageSourcePropType, ImageURISource } from 'react-native';
export default const ScaleImage = ({ desiredWidth, ...imageProps }: ScaleImageProps) => {
const [desiredHeight, setDesiredHeight] = useState(0);
const { source } = imageProps;
useEffect(() => {
if (isRequireSource(source)) {
const { width, height } = Image.resolveAssetSource(source);
setDesiredHeight((desiredWidth * height) / width);
} else if (isURISource(source)) {
Image.getSize(source.uri as string, (width, height) => {
setDesiredHeight((desiredWidth / width) * height);
});
} else if (isURISourceArray(source)) {
Image.getSize(source[0]?.uri as string, (width, height) => {
setDesiredHeight((desiredWidth / width) * height);
});
}
}, [source, desiredWidth]);
return (
<Image
{...imageProps}
style={[
imageProps.style,
{
width: desiredWidth,
height: desiredHeight,
},
]}
/>
);
};
interface ScaleImageProps extends ImageProps {
desiredWidth: number;
}
const isURISourceArray = (source: ImageSourcePropType): source is ImageURISource[] => {
return Array.isArray(source);
};
const isURISource = (source: ImageSourcePropType): source is ImageURISource => {
return !Array.isArray(source) && typeof source !== 'number';
};
const isRequireSource = (source: ImageSourcePropType): source is ImageRequireSource => {
return typeof source === 'number';
};
优化
绝大部分情况下,我们是能知道传入的是本地图片还是网络图片的,那就不必做判断的步骤了,因此可以分为两个组件
渲染本地图片
import { Image, ImageRequireSource, ImageStyle, StyleProp } from 'react-native';
export default function LocalImage({ source, desiredWidth, style }: LocalImageProps) {
const { width, height } = Image.resolveAssetSource(source);
return (
<SKComponent.Image
source={source}
style={[
{
width: desiredWidth,
height: (desiredWidth * height) / width,
},
style,
]}
/>
);
}
interface LocalImageProps {
source: ImageRequireSource;
desiredWidth: number;
style?: StyleProp<ImageStyle>;
}
渲染远程图片
import { useEffect, useState } from 'react';
import { Image, ImageStyle, StyleProp } from 'react-native';
export default function RemoteImage({ uri, desiredWidth, style }: RemoteImageProps) {
const [desiredHeight, setDesiredHeight] = useState(0);
useEffect(() => {
Image.getSize(uri, (width, height) => {
setDesiredHeight((desiredWidth / width) * height);
});
}, [uri, desiredWidth])
return (
<SKComponent.Image
source={{ uri }}
style={[
{
width: desiredWidth,
height: desiredHeight,
},
style,
]}
/>
);
}
interface RemoteImageProps {
uri: string;
desiredWidth: number;
style?: StyleProp<ImageStyle>;
}