React Native获取图片的真实大小
我们在项目开发中,为了能够让图片在我们前端显示的时候不会变形,我们通常会根据图片本身的真实宽高进行计算比例,这种计算之后再设置给图片的属性,就能够展示出我们理想的图片。
使用场景:
优化展示,等比缩放封装图片组件加载动画时,给容器指定适配图片宽高的大小图片自适应下面总结了下获取图片属性大小的方法
一、getSize我们在查阅资料后,能够发现官方提供了获取方案,静态方法 getSize()
。
使用过程
Image.getSize(uri, success, [failure]);
其中参数
uri:为图片资源地址success: 成功方法回调,返回包含width、heightfailure: 可选的失败方法回调 在显示图片前获取图片的宽高(以像素为单位)。如果图片地址不正确或下载失败,此方法也会失败。要获取图片的尺寸,首先需要加载或下载图片(同时会被缓存起来)。这意味着理论上你可以用这个方法来预加载图片,虽然此方法并没有针对这一用法进行优化,而且将来可能会换一些实现方案使得并不需要完整下载图片即可获取尺寸。所以更好的预加载方案是使用官方提供的那个专门的预加载方法Image.prefetch(url)
。
const image1 = require('@expo/snack-static/react-native-logo.png');
const image2 = { uri: 'http://www.kaotop.com/file/tupian/20220517/tiny_logo.png' };
const image3 = { uri: 'data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAADMAAAAzCAYAAAA6oTAqAAAAEXRFWHRTb2Z0d2FyZQBwbmdjcnVzaEB1SfMAAABQSURBVGje7dSxCQBACARB+2/ab8BEeQNhFi6WSYzYLYudDQYGBgYGBgYGBgYGBgYGBgZmcvDqYGBgmhivGQYGBgYGBgYGBgYGBgYGBgbmQw+P/eMrC5UTVAAAAABJRU5ErkJggg==' };
...
const [sizeValue1, updateSizeValue1] = useState({});
const [sizeValue2, updateSizeValue2] = useState({});
const [sizeValue3, updateSizeValue3] = useState({});
useEffect(() => {
// 本地静态文件
Image.getSize(image1, (width,height) => {
updateSizeValue1({ width,height });
},
(failure) => { console.log('failure', failure)});
// http远程文件
Image.getSize(image2.uri, (width,height) => {
updateSizeValue2({ width,height });
},
(failure) => { console.log('failure', failure)});
// base64文件
Image.getSize(image3.uri, (width,height) => {
updateSizeValue3({ width,height });
},
(failure) => { console.log('failure', failure)});
}, []);
...
最终我们会获取到图片的真实尺寸,然后调整我们图片展示
官方提供的另一方案是通过Image.resolveAssetSource(source)
获取资源大小。
Image.resolveAssetSource(source);
获取一个静态资源中图片的大小信息,返回一个包含width、height的结果
{
"__packager_asset":true,
"width":296,
"height":296,
"uri":"file:///var/mobile/Containers/Data/Application/6E50D62F-3DEE-47CC-A581-D5BC4CCD11FF/Library/Application%20Support/6669c7aafd21c14df3a1cdb8.png",
"scale":1
}
其中参数:
source 为图片资源const image1 = require('@expo/snack-static/react-native-logo.png');
const image2 = { uri: 'http://www.kaotop.com/file/tupian/20220517/tiny_logo.png' };
const image3 = { uri: 'data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAADMAAAAzCAYAAAA6oTAqAAAAEXRFWHRTb2Z0d2FyZQBwbmdjcnVzaEB1SfMAAABQSURBVGje7dSxCQBACARB+2/ab8BEeQNhFi6WSYzYLYudDQYGBgYGBgYGBgYGBgYGBgZmcvDqYGBgmhivGQYGBgYGBgYGBgYGBgYGBgbmQw+P/eMrC5UTVAAAAABJRU5ErkJggg==' };
...
const [assetSizeValue1, updateAssetSizeValue1] = useState({});
const [assetSizeValue2, updateAssetSizeValue2] = useState({});
const [assetSizeValue3, updateAssetSizeValue3] = useState({});
useEffect(() => {
const response1 = Image.resolveAssetSource(image1);
console.log('response1', response1)
updateAssetSizeValue1(response1, '', 2);
const response2 = Image.resolveAssetSource(image2);
updateAssetSizeValue2(response2, '', 2);
const response3 = Image.resolveAssetSource(image3);
updateAssetSizeValue3(response3, '', 2);
}, [])
最后获取到图片格式如下显示
能够看到只有静态图片资源能够解析出具体的宽高,在使用的过程中需要注意。
同时也能发现对于onLoad事件返回的参数也是符合我们的实际需求的,但是会存在性能上的一个问题,就是使用这种方案需要等等待图片加载完成后,才能获取到图片的真实大小。
onLoad={({ nativeEvent: {source: {width, height}}}) => setImageRealSize({width, height})}
获取一个静态资源中图片的大小信息,返回一个包含width、height的nativeEvent结果
{
"target":11009,
"source {
"height":296,
"width":296,
"uri":"file:///var/mobile/Containers/Data/Application/6E50D62F-3DEE-47CC-A581-D5BC4CCD11FF/Library/Application%20Support/882e38e3ef2dc9c7aafd21c14df3a1cdb8.png"
}
}
在项目中我们能够根据图片加载完成事件获取到当前资源的真实大小
import React, { useState, useEffect } from 'react';
import { ScrollView, View, Image, StyleSheet, Text } from 'react-native';
const styles = StyleSheet.create({
container: {
paddingTop: 50,
},
tinyLogo: {
width: 50,
height: 50,
},
logo: {
width: 66,
height: 58,
},
});
const image1 = require('@expo/snack-static/react-native-logo.png');
const image2 = { uri: 'http://www.kaotop.com/file/tupian/20220517/tiny_logo.png' };
const image3 = { uri: 'data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAADMAAAAzCAYAAAA6oTAqAAAAEXRFWHRTb2Z0d2FyZQBwbmdjcnVzaEB1SfMAAABQSURBVGje7dSxCQBACARB+2/ab8BEeQNhFi6WSYzYLYudDQYGBgYGBgYGBgYGBgYGBgZmcvDqYGBgmhivGQYGBgYGBgYGBgYGBgYGBgbmQw+P/eMrC5UTVAAAAABJRU5ErkJggg==' };
const DisplayAnImage = () => {
const [loadValue1, updateLoadValue1] = useState({});
const [loadValue2, updateLoadValue2] = useState({});
const [loadValue3, updateLoadValue3] = useState({});
useEffect(() => {
// 本地静态文件
Image.getSize(image1, (width,height) => {
updateSizeValue1({ width,height });
},
(failure) => { console.log('failure', failure)});
// http远程文件
Image.getSize(image2.uri, (width,height) => {
updateSizeValue2({ width,height });
},
(failure) => { console.log('failure', failure)});
// base64文件
Image.getSize(image3.uri, (width,height) => {
updateSizeValue3({ width,height });
},
(failure) => { console.log('failure', failure)});
}, []);
return (
<ScrollView style={styles.container} contentContainerStyle={{ paddingVertical: 80, marginHorizontal: 10}}>
<Image
// style={styles.tinyLogo}
source={image1}
onLoad={(e) => {
const nativeEvent = e.nativeEvent;
console.log('event', nativeEvent);
updateLoadValue1(nativeEvent);
}
}
onError={
(error) => {
console.log('error', error.toString())
}
}
/>
<Image
style={styles.tinyLogo}
source={image2}
onLoad={(e) => {
const nativeEvent = e.nativeEvent;
updateLoadValue2(nativeEvent);
}
}
/>
<Image
style={styles.logo}
source={image3}
onLoad={(e) => {
const nativeEvent = e.nativeEvent;
updateLoadValue3(nativeEvent);
}
}
/>
<View style={{ backgroundColor: '#f0ff99'}}>
<Text>{JSON.stringify(loadValue1, '', 2)}</Text>
<Text>{JSON.stringify(loadValue2, '' ,2 )}</Text>
<Text>{JSON.stringify(loadValue3, '', 2)}</Text>
</View>
</ScrollView>
);
}
export default DisplayAnImage;
最后获取到图片格式如下显示,在获取到之后,我们能够使用更新state的方式进行更新当前图片大小的设置。
以上就是获取图片真实大小的三种方案。
对于有些小伙伴来说,可能并不是用的React Native提供的组件Image,而是使用的三方组件react-native-fast-image,对于该组件,我们也能够使用组合以上方案获取真实大小。
四、react-native-fast-image中的组合实践 由于该三方库需要在首次布局的时候就需要设置宽高,则我们更多的推荐使用Image.getSize()
方案进行解决针对onLoad函数,该库中ios平台和android平台存在差异性,具体表现在返回的json结构中,ios平台缺失source字段,我们可以通过以下方案抹平
onLoad: (e) => void = (e) => {
const { baseWidth = 690 } = this.props;
const height = Platform.OS === 'android' ? ((e.nativeEvent?.source?.height * baseWidth) / e.nativeEvent?.source?.width) : (e.nativeEvent.height * baseWidth) / e.nativeEvent.width
const imgStyle = {
width: baseWidth,
height: height || baseWidth
};
this.setState({ imgStyle: imgStyle })
};
五、完整案例代码与演示
import React, { useState, useEffect } from 'react';
import { ScrollView, View, Image, StyleSheet, Text } from 'react-native';
const styles = StyleSheet.create({
container: {
paddingTop: 50,
},
tinyLogo: {
width: 50,
height: 50,
},
logo: {
width: 66,
height: 58,
},
});
const image1 = require('@expo/snack-static/react-native-logo.png');
const image2 = { uri: 'http://www.kaotop.com/file/tupian/20220517/tiny_logo.png' };
const image3 = { uri: 'data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAADMAAAAzCAYAAAA6oTAqAAAAEXRFWHRTb2Z0d2FyZQBwbmdjcnVzaEB1SfMAAABQSURBVGje7dSxCQBACARB+2/ab8BEeQNhFi6WSYzYLYudDQYGBgYGBgYGBgYGBgYGBgZmcvDqYGBgmhivGQYGBgYGBgYGBgYGBgYGBgbmQw+P/eMrC5UTVAAAAABJRU5ErkJggg==' };
const DisplayAnImage = () => {
const [sizeValue1, updateSizeValue1] = useState({});
const [sizeValue2, updateSizeValue2] = useState({});
const [sizeValue3, updateSizeValue3] = useState({});
const [assetSizeValue1, updateAssetSizeValue1] = useState({});
const [assetSizeValue2, updateAssetSizeValue2] = useState({});
const [assetSizeValue3, updateAssetSizeValue3] = useState({});
const [loadValue1, updateLoadValue1] = useState({});
const [loadValue2, updateLoadValue2] = useState({});
const [loadValue3, updateLoadValue3] = useState({});
useEffect(() => {
// 本地静态文件
Image.getSize(image1, (width,height) => {
updateSizeValue1({ width,height });
},
(failure) => { console.log('failure', failure)});
// http远程文件
Image.getSize(image2.uri, (width,height) => {
updateSizeValue2({ width,height });
},
(failure) => { console.log('failure', failure)});
// base64文件
Image.getSize(image3.uri, (width,height) => {
updateSizeValue3({ width,height });
},
(failure) => { console.log('failure', failure)});
}, []);
useEffect(() => {
const response1 = Image.resolveAssetSource(image1);
console.log('response1', response1)
updateAssetSizeValue1(response1, '', 2);
const response2 = Image.resolveAssetSource(image2);
updateAssetSizeValue2(response2, '', 2);
const response3 = Image.resolveAssetSource(image3);
updateAssetSizeValue3(response3, '', 2);
}, [])
return (
<ScrollView style={styles.container} contentContainerStyle={{ paddingVertical: 80, marginHorizontal: 10}}>
<Image
// style={styles.tinyLogo}
source={image1}
onLoad={(e) => {
const nativeEvent = e.nativeEvent;
console.log('event', nativeEvent);
updateLoadValue1(nativeEvent);
}
}
onError={
(error) => {
console.log('error', error.toString())
}
}
/>
<Image
style={styles.tinyLogo}
source={image2}
onLoad={(e) => {
const nativeEvent = e.nativeEvent;
updateLoadValue2(nativeEvent);
}
}
/>
<Image
style={styles.logo}
source={image3}
onLoad={(e) => {
const nativeEvent = e.nativeEvent;
updateLoadValue3(nativeEvent);
}
}
/>
<View style={{ marginTop: 10, backgroundColor: '#009977'}}>
<Text>{JSON.stringify(sizeValue1)}</Text>
<Text>{JSON.stringify(sizeValue2)}</Text>
<Text>{JSON.stringify(sizeValue3)}</Text>
</View>
<View style={{ backgroundColor: '#fafafa'}}>
<Text>{JSON.stringify(assetSizeValue1, '', 2)}</Text>
<Text>{JSON.stringify(assetSizeValue2, '', 2)}</Text>
<Text>{JSON.stringify(assetSizeValue3, '', 2)}</Text>
</View>
<View style={{ backgroundColor: '#f0ff99'}}>
<Text>{JSON.stringify(loadValue1, '', 2)}</Text>
<Text>{JSON.stringify(loadValue2, '' ,2 )}</Text>
<Text>{JSON.stringify(loadValue3, '', 2)}</Text>
</View>
</ScrollView>
);
}
export default DisplayAnImage;
戳进来瞧瞧-> Expo Go
参考资料:
https://www.react-native.cn/docs/image#onloadhttps://www.react-native.cn/docs/image#resolveassetsourcehttps://github.com/DylanVann/react-native-fast-image欢迎分享,转载请注明来源:内存溢出
评论列表(0条)