在React Native中进行渐进式图像加载

​Internet连接速度是可变的,尤其是在使用移动设备时。作为开发人员,我们经常会忘记许多用户在功能较弱的设备上运行我们的应用程序,并且互联网连接速度比我们慢。走出深山,尝试访问您的应用程序并查看其性能。

应用程序中最昂贵的东西之一就是您正在加载的远程图像。它们将花费一些时间来加载,特别是如果它们是大图像。

今天,我们将构建一个组件,使我们能够:

  1. 传递完整尺寸的图像以显示(就像普通Image组件一样)

  2. 在加载全尺寸图片时传递缩略图以显示

  3. 在即将下载的图片的位置自动显示一个占位符,以指示有东西存在

  4. 在每个状态之间进行动画处理。

Getting Started

要开始创建一个新的阵营原生应用(通过react-native initcreate-react-native-appexpoCLI),并添加以下App.js

 1import React from 'react';
 2import { StyleSheet, View, Dimensions, Image } from 'react-native';
 3
 4const w = Dimensions.get('window');
 5
 6const styles = StyleSheet.create({
 7  container: {
 8    flex: 1,
 9    alignItems: 'center',
10    justifyContent: 'center',
11  },
12});
13
14export default class App extends React.Component {
15  render() {
16    return (
17      <View style={styles.container}>
18        <Image
19          source={{ uri: `https://images.pexels.com/photos/671557/pexels-photo-671557.jpeg?w=${w.width * 2}&buster=${Math.random()}` }}
20          style={{ width: w.width, height: w.width }}
21          resizeMode="cover"
22        />
23      </View>
24    );
25  }
26}
27

该代码块仅显示图像。它要求Pexels提供2 倍于屏幕大小的图像(我们想要大图像,以便它们缓慢加载),而buster查询参数将帮助我们不缓存图像,因此我们可以完全了解发生了什么。您不想在实际的应用程序中执行此操作。

Feel the Pain

就像我之前说过的-作为开发人员,您可能已经拥有相当不错的互联网连接。

让我们改变它。

如果您使用的是Mac,则可以安装一个称为Network Link Conditioner的工具(以下是安装方法)。我确定Windows和Linux的粉丝也有相似之处(如果您有工具建议,请参见下面的提示)。

它将允许您模拟几乎所需的任何网络状况。请记住在开始流式传输视频之前将其关闭。

启用它并将其设置为“ 3G”配置文件。

这是关闭和打开网络链接调节器的启动器的比较。

网络链接调节器关闭与网络链接调节器打开(3G速度)

ProgressiveImage Component

要替换该Image组件,我们将创建一个名为的新组件ProgressiveImage。该组件的目标是使它能够像普通Image组件一样正常工作,只是具有一些附加功能:

  1. 彩色背景填充图像的位置

  2. 能够传递缩略图

首先,让我们建立基础:

1// ProgressiveImage.jsimport React from 'react';
2import { View, StyleSheet, Image } from 'react-native';const styles = StyleSheet.create({});class ProgressiveImage extends React.Component {
3  render() {
4    return <Image {...this.props} />
5  }
6}export default ProgressiveImage;
7

我们正在使用传播语法将所有内容传递this.propsImage组件,以使一切都能按预期工作,而无需手动定义每个道具。

然后替换ImageApp.js用新的ProgressiveImage组件。

 1// App.jsimport React from 'react';
 2import { StyleSheet, View, Dimensions } from 'react-native';
 3import ProgressiveImage from './ProgressiveImage';// ...export default class App extends React.Component {
 4  render() {
 5    return (
 6      <View style={styles.container}>
 7        <ProgressiveImage
 8          source={{ uri: `https://images.pexels.com/photos/671557/pexels-photo-671557.jpeg?w=${w.width * 2}&buster=${Math.random()}` }}
 9          style={{ width: w.width, height: w.width }}
10          resizeMode="cover"
11        />
12      </View>
13    );
14  }
15}
16

一切都应该与以前完全一样。

设置背景色

加载远程图像时,需要指定图像的宽度和高度以进行渲染。我们将利用该要求使我们能够轻松设置默认背景色。

 1import React from 'react';
 2import { View, StyleSheet, Image } from 'react-native';const styles = StyleSheet.create({
 3  imageOverlay: {
 4    position: 'absolute',
 5    left: 0,
 6    right: 0,
 7    bottom: 0,
 8    top: 0,
 9  },
10  container: {
11    backgroundColor: '#e1e4e8',
12  },
13});class ProgressiveImage extends React.Component {
14  render() {
15    return (
16      <View style={styles.container}>
17        <Image {...this.props} />
18      </View>
19    );
20  }
21}export default ProgressiveImage;
22

首先,我们container使用背景颜色创建样式,然后将Image组件包装为View(分配了新样式)。

这为我们逐步加载图像提供了第一阶段。

显示缩略图图像

接下来,我们将显示图像的缩略图。生成此图像超出了本教程的范围,因此我们假设您已经获得了完整尺寸的图像和缩略图版本。

首先,在的实例中ProgressiveImage,我们将添加一个thumbnailSource道具,该道具将获取与典型Image源道具完全相同的信息。在其中,我们将传递图像的较小版本(在这种情况下为50,请使用所需的任何值)和我们的清除缓存查询变量(仅用于演示目的)。

 1// App.js// ...export default class App extends React.Component {
 2  render() {
 3    return (
 4      <View style={styles.container}>
 5        <ProgressiveImage
 6          thumbnailSource={{ uri: `https://images.pexels.com/photos/671557/pexels-photo-671557.jpeg?w=50&buster=${Math.random()}` }}
 7          source={{ uri: `https://images.pexels.com/photos/671557/pexels-photo-671557.jpeg?w=${w.width * 2}&buster=${Math.random()}` }}
 8          style={{ width: w.width, height: w.width }}
 9          resizeMode="cover"
10        />
11      </View>
12    );
13  }
14}
15

然后,我们将修改我们的ProgressiveImage组件。首先将imageOverlay样式添加到样式对象。

 1// ProgressiveImage.js// ...const styles = StyleSheet.create({
 2  imageOverlay: {
 3    position: 'absolute',
 4    left: 0,
 5    right: 0,
 6    bottom: 0,
 7    top: 0,
 8  },
 9  container: {
10    backgroundColor: '#e1e4e8',
11  },
12});// ...

然后,我们将渲染两个Image组件。在此之前,尽管我们将使用对象分解来提取一些道具,this.props因为我们将覆盖/组合它们。

 1// ProgressiveImage.js// ...class ProgressiveImage extends React.Component {
 2  render() {
 3    const {
 4      thumbnailSource,
 5      source,
 6      style,
 7      ...props
 8    } = this.props;    return (
 9      <View style={styles.container}>
10        <Image
11          {...props}
12          source={thumbnailSource}
13          style={style}
14        />
15        <Image
16          {...props}
17          source={source}
18          style={[styles.imageOverlay, style]}
19        />
20      </View>
21    );
22  }
23}export default ProgressiveImage;

你可以看到我们拉thumbnailSourcesourcestyle道具了。然后,我们使用“ rest”语法捕获其余的道具。这使我们可以将所有通用道具转发到我们的两个图像,而仅将所需道具转发到正确的组件(如适当的来源)。

您会注意到,我们正在将传递的样式和我们styles.imageOverlay的全尺寸图片结合在一起。这样,通过绝对定位,图像将覆盖缩略图版本。

结果如下:

注意:您会注意到缩略图图像非常像素化。您可以将blurRadius道具传递到缩略图图像以使其模糊。我拍了一个屏幕截图,以便您可以看到不同之处(我在示例中使用的是blurRadius 2)。

您还会注意到,如果我们不将a传递thumbnailSourceProgressiveImage组件,则一切正常,这意味着即使我们没有缩略图,我们也可以将其用于所有远程图像。

动画过渡

我们要做的最后一件事是使背景颜色,缩略图图像和全尺寸图像之间的过渡变得平滑。为此,我们将使用AnimatedReact Native中的库。

一旦导入Animated,你会再想更换Image的部件ProgressiveImageAnimated.Image

您还需要在组件上创建两个新的动画变量,默认将它们设置为0。

 1// ProgressiveImage.jsimport React from 'react';
 2import { View, StyleSheet, Animated } from 'react-native';// ...class ProgressiveImage extends React.Component {
 3  thumbnailAnimated = new Animated.Value(0);  imageAnimated = new Animated.Value(0);  render() {
 4    const {
 5      thumbnailSource,
 6      source,
 7      style,
 8      ...props
 9    } = this.props;    return (
10      <View style={styles.container}>
11        <Animated.Image
12          {...props}
13          source={thumbnailSource}
14          style={style}
15          blurRadius={2}
16        />
17        <Animated.Image
18          {...props}
19          source={source}
20          style={[styles.imageOverlay, style]}
21        />
22      </View>
23    );
24  }
25}

这些Animated.Value将用于驱动图像的不透明度。加载缩略图时,我们将设置thumbnailAnimated为1。加载全尺寸图片时,我们将设置imageAnimated为1。

 1// ProgressiveImage.js// ...class ProgressiveImage extends React.Component {
 2  thumbnailAnimated = new Animated.Value(0);  imageAnimated = new Animated.Value(0);  handleThumbnailLoad = () => {
 3    Animated.timing(this.thumbnailAnimated, {
 4      toValue: 1,
 5    }).start();
 6  }  onImageLoad = () => {
 7    Animated.timing(this.imageAnimated, {
 8      toValue: 1,
 9    }).start();
10  }  // ...
11}

这些功能将通过组件的onLoad属性调用Animated.Image

 1// ProgressiveImage.js// ...class ProgressiveImage extends React.Component {
 2  // ...  render() {
 3    const {
 4      thumbnailSource,
 5      source,
 6      style,
 7      ...props
 8    } = this.props;    return (
 9      <View style={styles.container}>
10        <Animated.Image
11          {...props}
12          source={thumbnailSource}
13          style={[style, { opacity: this.thumbnailAnimated }]}
14          onLoad={this.handleThumbnailLoad}
15          blurRadius={1}
16        />
17        <Animated.Image
18          {...props}
19          source={source}
20          style={[styles.imageOverlay, { opacity: this.imageAnimated }, style]}
21          onLoad={this.onImageLoad}
22        />
23      </View>
24    );
25  }
26}export default ProgressiveImage;

这将导致最终的渐进式图像加载。

 

最终代码可以在Github(https://github.com/HandlebarLabs/react-native-examples-and-tutorials/tree/master/tutorials/progressive-image-loading)上找到。

 

欢迎关注公众号

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值