Skip to main content

Progressive Image Loading in React Native

Getting Started

import React from 'react';
import { StyleSheet, View, Dimensions, Image } from 'react-native';

const w = Dimensions.get('window');

const styles = StyleSheet.create({
  container: {
    flex: 1,
    alignItems: 'center',
    justifyContent: 'center',
  },
});

export default class App extends React.Component {
  render() {
    return (
      <View style={styles.container}>
        <Image
          source={{ uri: `https://images.pexels.com/photos/671557/pexels-photo-671557.jpeg?w=${w.width * 2}&buster=${Math.random()}` }}
          style={{ width: w.width, height: w.width }}
          resizeMode="cover"
        />
      </View>
    );
  }
}

Feel the Pain

Let’s change that.It will allow you to simulate just about any network condition you need. Just remember to turn it off before you start trying to stream video.
Enable it and set it to the “3G” profile.
Network Link Conditioner
Here’s a comparison of the starter with Network Link Conditioner off vs. on.
Network Link Conditioner OFF vs. Network Link Conditioner ON (3G Speeds)

ProgressiveImage Component

  1. Colored background filling in where the image will be
  2. Ability to pass a thumbnail image
First let’s put the foundation in place:
// ProgressiveImage.jsimport React from 'react';
import { View, StyleSheet, Image } from 'react-native';const styles = StyleSheet.create({});class ProgressiveImage extends React.Component {
  render() {
    return <Image {...this.props} />
  }
}export default ProgressiveImage;
We’re using the spread syntax to pass all of this.props down to the Image component so that everything works as expected without us having to manually define each and every prop.
Then replace Image in App.js with the new ProgressiveImagecomponent.
// App.jsimport React from 'react';
import { StyleSheet, View, Dimensions } from 'react-native';
import ProgressiveImage from './ProgressiveImage';// ...export default class App extends React.Component {
  render() {
    return (
      <View style={styles.container}>
        <ProgressiveImage
          source={{ uri: `https://images.pexels.com/photos/671557/pexels-photo-671557.jpeg?w=${w.width * 2}&buster=${Math.random()}` }}
          style={{ width: w.width, height: w.width }}
          resizeMode="cover"
        />
      </View>
    );
  }
}
Everything should work exactly the same as it did before.

Setting the Background Color

import React from 'react';
import { View, StyleSheet, Image } from 'react-native';const styles = StyleSheet.create({
  imageOverlay: {
    position: 'absolute',
    left: 0,
    right: 0,
    bottom: 0,
    top: 0,
  },
  container: {
    backgroundColor: '#e1e4e8',
  },
});class ProgressiveImage extends React.Component {
  render() {
    return (
      <View style={styles.container}>
        <Image {...this.props} />
      </View>
    );
  }
}export default ProgressiveImage;
First we create a container style with our background color and then wrap the Image component in a View (with the new style assigned to it).
This gives us the first phase of our progressive image loading.
Background Color While Image is Loading

Displaying Thumnail Image

First, to our instance of ProgressiveImage, we'll add a thumbnailSource prop which will take the exact same information as a typical Image source prop. In it we'll pass a smaller version of the image (50 in this case, use whatever you want) and our cache-busting query variable (for demo purposes only).
// App.js// ...export default class App extends React.Component {
  render() {
    return (
      <View style={styles.container}>
        <ProgressiveImage
          thumbnailSource={{ uri: `https://images.pexels.com/photos/671557/pexels-photo-671557.jpeg?w=50&buster=${Math.random()}` }}
          source={{ uri: `https://images.pexels.com/photos/671557/pexels-photo-671557.jpeg?w=${w.width * 2}&buster=${Math.random()}` }}
          style={{ width: w.width, height: w.width }}
          resizeMode="cover"
        />
      </View>
    );
  }
}
Then we’ll modify our ProgressiveImage component. First add an imageOverlay style to your styles object.
// ProgressiveImage.js// ...const styles = StyleSheet.create({
  imageOverlay: {
    position: 'absolute',
    left: 0,
    right: 0,
    bottom: 0,
    top: 0,
  },
  container: {
    backgroundColor: '#e1e4e8',
  },
});// ...
We’ll then render two Image components. Before we do that though we're going to use object destructuring to pull a few props off of this.props because we'll be overriding/combining them.
// ProgressiveImage.js// ...class ProgressiveImage extends React.Component {
  render() {
    const {
      thumbnailSource,
      source,
      style,
      ...props
    } = this.props;    return (
      <View style={styles.container}>
        <Image
          {...props}
          source={thumbnailSource}
          style={style}
        />
        <Image
          {...props}
          source={source}
          style={[styles.imageOverlay, style]}
        />
      </View>
    );
  }
}export default ProgressiveImage;
You can see we pull thumbnailSourcesource, and style off of props. We then use "rest" syntax to capture the rest of the props. This allows us to forward all general purpose props to both of our images and only forward required props to the right component (like the appropriate source).
You’ll notice that we’re combining the passed style and our styles.imageOverlay in the full size image. This is so that, via absolute positioning, the image will coverup the thumbnail version.
Here’s the result:
Thumbnail and Full Size Image Displayed
Note: You’ll noticed that the thumbnail image is quite pixelated. You can pass a blurRadius prop to the thumbnail image to blur it. I took a screenshot so you can see the difference (I'm using a blurRadius of 2 for the example).
Without blurRadius vs. With blurRadius
You’ll also notice that if we don’t pass a thumbnailSource to the ProgressiveImage component everything works fine which means we can use this component for all of remote images even if we don't have a thumbnail image.

Animating the Transition

Once you’ve imported Animated you'll then want replace the Imagecomponents in ProgressiveImage with Animated.Image.
You’ll also want to create two new animated variables on the component, defaulting them to 0.
// ProgressiveImage.jsimport React from 'react';
import { View, StyleSheet, Animated } from 'react-native';// ...class ProgressiveImage extends React.Component {
  thumbnailAnimated = new Animated.Value(0);  imageAnimated = new Animated.Value(0);  render() {
    const {
      thumbnailSource,
      source,
      style,
      ...props
    } = this.props;    return (
      <View style={styles.container}>
        <Animated.Image
          {...props}
          source={thumbnailSource}
          style={style}
          blurRadius={2}
        />
        <Animated.Image
          {...props}
          source={source}
          style={[styles.imageOverlay, style]}
        />
      </View>
    );
  }
}
These Animated.Values will be used to drive the opacity of the images. When the thumbnail loads we'll set thumbnailAnimated to 1. When the full size image loads we'll set imageAnimated to 1.
// ProgressiveImage.js// ...class ProgressiveImage extends React.Component {
  thumbnailAnimated = new Animated.Value(0);  imageAnimated = new Animated.Value(0);  handleThumbnailLoad = () => {
    Animated.timing(this.thumbnailAnimated, {
      toValue: 1,
    }).start();
  }  onImageLoad = () => {
    Animated.timing(this.imageAnimated, {
      toValue: 1,
    }).start();
  }  // ...
}
These functions will be called via the onLoad prop of the Animated.Image components.
// ProgressiveImage.js// ...class ProgressiveImage extends React.Component {
  // ...  render() {
    const {
      thumbnailSource,
      source,
      style,
      ...props
    } = this.props;    return (
      <View style={styles.container}>
        <Animated.Image
          {...props}
          source={thumbnailSource}
          style={[style, { opacity: this.thumbnailAnimated }]}
          onLoad={this.handleThumbnailLoad}
          blurRadius={1}
        />
        <Animated.Image
          {...props}
          source={source}
          style={[styles.imageOverlay, { opacity: this.imageAnimated }, style]}
          onLoad={this.onImageLoad}
        />
      </View>
    );
  }
}export default ProgressiveImage;
That results in our final progressive image loading.
Final Demo
The final code can be found on Github.

Comments

  1. If you're attempting to lose kilograms then you certainly have to start following this totally brand new personalized keto meal plan diet.

    To create this keto diet service, licensed nutritionists, personal trainers, and professional cooks united to develop keto meal plans that are useful, decent, cost-efficient, and satisfying.

    Since their grand opening in early 2019, 100's of individuals have already completely transformed their body and health with the benefits a proper keto meal plan diet can offer.

    Speaking of benefits: clicking this link, you'll discover 8 scientifically-tested ones provided by the keto meal plan diet.

    ReplyDelete

Post a Comment

Thank You.

Popular Posts

What are the Alternatives of device UDID in iOS? - iOS7 / iOS 6 / iOS 5 – Get Device Unique Identifier UDID

Get Device Unique Identifier UDID Following code will help you to get the unique-device-identifier known as UDID. No matter what iOS user is using, you can get the UDID of the current iOS device by following code. - ( NSString *)UDID { NSString *uuidString = nil ; // get os version NSUInteger currentOSVersion = [[[[[UIDevice currentDevice ] systemVersion ] componentsSeparatedByString: @" . " ] objectAtIndex: 0 ] integerValue ]; if (currentOSVersion <= 5 ) { if ([[ NSUserDefaults standardUserDefaults ] valueForKey: @" udid " ]) { uuidString = [[ NSUserDefaults standardDefaults ] valueForKey: @" udid " ]; } else { CFUUIDRef uuidRef = CFUUIDCreate ( kCFAllocatorDefault ); uuidString = ( NSString *) CFBridgingRelease ( CFUUIDCreateString ( NULL ,uuidRef)); CFRelease (uuidRef); [[ NSUserDefaults standardUserDefaults ] setObject: uuidString ForKey: @" udid " ]; [[ NSUserDefaults standardUserDefaults ] synchro...

An introduction to Size Classes for Xcode 8

Introduction to Size Classes for Xcode In iOS 8, Apple introduced  size classes , a way to describe any device in any orientation. Size classes rely heavily on auto layout. Until iOS 8, you could escape auto layout. IN iOS8, Apple changed several UIKit classes to depend on size classes. Modal views, popovers, split views, and image assets directly use size classes to determine how to display an image. Identical code to present a popover on an iPad  causes a iPhone to present a modal view. Different Size Classes There are two sizes for size classes:  compact , and  regular . Sometime you’ll hear about any.  Any  is the generic size that works with anything. The default Xcode layout, is  width:any height:any . This layout is for all cases. The Horizontal and vertical dimensions are called  traits , and can be accessed in code from an instance of  UITraitCollection . The  compact  size descr...

How To Transfer your app to another iOS Developer Account - iTunes

Transfer your app to another iOS Developer Account Apple states all apps must be submitted by the provider of the app’s content using their own, unique Apple Developer Account, this also applies to existing, already published apps. If you currently have one or more apps published on your developer account, the Apple review will now include this check when you submit a store update. In order to avoid any disruptions in the availability of your app in the iOS store, please make sure all your apps are published on a matching developer account. If the app and the developer account do not match, Apple will potentially reject the update. Your older version of the app will remain available. Fortunately it is possible, and relatively simple, to transfer the ownership of an app to another developer without removing the app from the App Store. When an app is transferred it will keep its reviews and ratings and users will still have access to future updates. You can transfer multi...