React Native获取图片的真实大小自适应

React Native获取图片的真实大小

我们在项目开发中,为了能够让图片在我们前端显示的时候不会变形,我们通常会根据图片本身的真实宽高进行计算比例,这种计算之后再设置给图片的属性,就能够展示出我们理想的图片。

使用场景:

  • 优化展示,等比缩放
  • 封装图片组件加载动画时,给容器指定适配图片宽高的大小
  • 图片自适应

下面总结了下获取图片属性大小的方法

一、getSize

我们在查阅资料后,能够发现官方提供了获取方案,静态方法 getSize()

使用过程

Image.getSize(uri, success, [failure]);

其中参数

  • uri:为图片资源地址
  • success: 成功方法回调,返回包含width、height
  • failure: 可选的失败方法回调
  1. 在显示图片前获取图片的宽高(以像素为单位)。如果图片地址不正确或下载失败,此方法也会失败。
  2. 要获取图片的尺寸,首先需要加载或下载图片(同时会被缓存起来)。这意味着理论上你可以用这个方法来预加载图片,虽然此方法并没有针对这一用法进行优化,而且将来可能会换一些实现方案使得并不需要完整下载图片即可获取尺寸。所以更好的预加载方案是使用官方提供的那个专门的预加载方法Image.prefetch(url)
const image1 = require('@expo/snack-static/react-native-logo.png');
const image2 = { uri: 'https://reactnative.dev/img/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)获取资源大小。

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: 'https://reactnative.dev/img/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事件返回的参数也是符合我们的实际需求的,但是会存在性能上的一个问题,就是使用这种方案需要等等待图片加载完成后,才能获取到图片的真实大小。


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: 'https://reactnative.dev/img/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: 'https://reactnative.dev/img/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


参考资料:

  1. https://www.react-native.cn/docs/image#onload
  2. https://www.react-native.cn/docs/image#resolveassetsource
  3. https://github.com/DylanVann/react-native-fast-image
  • 1
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

suwu150

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值