Preload images in Ionic using $ImageCacheFactory

posted in: Uncategorized | 30

Images are easily one of the biggest slow downs of modern websites due to their size, often being as big as many megabytes. These images also take time to load and render, meaning your users may experience a visual delay in your page loading.

In hybrid it is a similar story. Regardless of if images are loading from an external website or from the local file system, these images can take time to load in giving your app a less smooth feel as images pop in on demand.

The solution? Preloading images.

Preloading images involves priming the clients cache with the images you will need to display at some point in your app. This article will cover how to do this using the Ionic Ion $ImageCacheFactory (on GitHub) and how this ion works.

Note: This module will work with vanilla Angular as it does not rely on any Ionic components or code.

Setup

Before we can use this module, we’ll need to grab it off of GitHub. Then, we’ll need to include the script file in our app.

<script src="lib/ionic.ion.imagecachefactory.js"></script>

Next, we’ll need to import the module into our app module:

angular.module('myCoolApp', ['ionic','ionic.ion.imageCacheFactory'])

Finally, in the controller (assuming here) that you want to use it, include it in the dependency injection:

.controller("MyCtrl",function($ImageCacheFactory){

});

Now we can start using it.

Basic Syntax

The basic syntax for $ImageCacheFactory is as follows:

$ImageCacheFactory.Cache([]);

The Cache method accepts one parameter which is an array of images locations. For example:

$ImageCacheFactory.Cache([
        "http://domain.com/path/to/kittens.jpg",
        "http://domain.com/path/to/kittens2.jpg",
        "http://domain.com/path/to/kittens3.jpg"
    ]);

The method also returns a promise when all images have been loaded, just in case you want to handle that.

$ImageCacheFactory.Cache([
        "http://domain.com/path/to/kittens.jpg",
        "http://domain.com/path/to/kittens2.jpg",
        "http://domain.com/path/to/kittens3.jpg"
    ]).then(function(){
        console.log("Images done loading!");
    });

If any of the images result in an error, you can handle that as well by passing a second function to your then.

$ImageCacheFactory.Cache([
        "http://domain.com/path/to/kittens.jpg",
        "http://domain.com/path/to/kittens2.jpg",
        "http://domain.com/path/to/kittens3.jpg"
    ]).then(function(){
        console.log("Images done loading!");
    },function(failed){
        console.log("An image filed: "+failed);
    });

Preload All the Images

Use Case #1: Preload All App Images

If your app is very image heavy and you know ahead of time what all of those images will be, it may help the perceived performance of your app if you preload all of those images when your app starts up. To do this, we can call our $ImageCacheFactory in the run function of our app module.

angular.module('ionicApp', ['ionic','ionic.ion.imageCacheFactory'])
.run(function($ImageCacheFactory){
  $ImageCacheFactory.Cache(["http://domain.com/path/to/kittens.jpg"]).then(function(){
    console.log("done preloading!");
  });
})

Example on CodePen

Use Case #2: Master Detail Pattern

One pretty common scenario is that you have a Master Detail Pattern in your app and on the details view there is an image. Normally, when you navigate from the master list to the detail, you can visibly see the image load in, especially if it is a large image. This is not ideal as it gives it a very “web” like look to it when you can see the image loading top to bottom, row by row of pixels.

Instead, you may find it useful to preload the images for the details views asynchronously when you land on the master list view.

.controller("MasterCtrl",function($scope, $ImageCacheFactory, PeopleService){
	PeopleService.GetPeople().then(function(data){
		$scope.people = data;
		
		//Build the list of images to preload
		var images = [];
		for(var i=0; i<data.length;i++){
			images.push(data[i].image);
		}
		
		$ImageCacheFactory.Cache(images);
	});
});

Conclusion

Using this module, you can improve the perceived performance of your app and improve the native feel by making sure things are loaded before your user gets to them. Any questions? Feel free to comments below.

My name is Andrew McGivery. I currently work full time as an application developer at Manulife Financial in Canada. My current passion is building and leading highly engaged teams where employee happiness, learning, and growth is a priority.

30 Responses

  1. cfjedimaster

    A few questions. You called this an Ion, but why isn’t it listed in “ionic ions”? Second, why didn’t you use “ionic add” to add the Ion?

    • Not sure if this will answer both questions or just the first one, but the Ionic Ions site doesn’t seem to be actively maintained. Infact, at the time of writing this comment, it doesn’t appear to even be online.

      Also, a clarification. The Ionic guys have defined an Ion as anything that is a module meant for Ionic that is not part of the core framework.

  2. cfjedimaster

    Hmm, ok. I’ll bug them on Twitter.

  3. Hey Andrew,

    This is fantastic, thanks!

    Two questions, if I may:
    (1) Each image has it’s own promise, and they are all passed into $q.all. So the Ion’s success function will be called only once they are all resolved.. If one of them fails though, it’ll never fire the success handler, right? $q’s documentation for all() says: “If any of the promises is resolved with a rejection, this resulting promise will be rejected with the same rejection value.” So there’s no way to know when all images have either finished loading or finished failing. I suppose this could be added though, by maintaining a list in the factory and manually tracking them through the onload and onerror callbacks.

    (2) How do we know for certain that Cordova is caching the image and won’t try to load it from the server again when I reference it later in a view? Do you know if this cache persists for the life of this instance of the app?

    I love the blog, please keep the posts coming!

    John
    (in Waterloo)

    • Hey John,

      Thanks for your compliment. Always nice to see someone from Canada (especially KW). 🙂

      1. Your assumption is correct. That aspect of the ion could for sure be improved.
      2. From experience, the cache does persist throughout the app. We used a method like this to preload images from a master list that were to be displayed on a details view and it worked very well.

      Thanks again for your comment!

  4. Hello, thanks for the article, how is it different from ImgCache (https://github.com/jBenes/angular-imgcache.js/tree/master)

    • From the library that library is based on (https://github.com/chrisben/imgcache.js):

      “This library is especially useful for mobile web applications using Phonegap/Cordova where the normal browser cache cannot be relied upon and where offline navigation is quite common.”

      $ImageCacheFactory uses the browser cache whereas imgcache uses the HTML5 file API.

  5. Brian Tan

    Hi Andrew, have you encountered instances where the pre-loading technique shown in your $imagecachefactory would fail ? If yes, what would those circumstances be ?

    For example, it doesn’t seem to work when I don’t call the cache function inside the app.module().run() function and instead calling it somewhere else.

    And how many images or cache size can the browser cache handle ? Is this limit a configuration that we can change ? If yes, how ?

    Appreciate some feedbacks here. The code in the github seems simple enough to understand but at the same time raises a lot of alarms in my mind because it could be relying on a lot of implicit behaviors behind the scene, many of which may not be obvious.

  6. Hi Andrew!
    Thank you very much for sharing your knowledge, I’m really enjoying it.

    One question:
    I have installed your module and when I tested it in the browser it didn’t worked, but in the Ionic view it’s working fine. Is this possible?, Could you explain me grosso modo what’s the difference between them?

    I’m just curious about it.

    Thanks!

  7. Any idea of how to preload google maps ?

  8. How about preload YouTube video (or videos from Wista or Viddler) using this Angular YouTube directive? https://github.com/brandly/angular-youtube-embed

  9. Also. Can I preload images which are loaded as background image via scss / css?

  10. I think I also found a problem. I when load image from local img folder like so at the run:
    $ImageCacheFactory.Cache([
    “../img/signin.jpg”,
    “../img/signup.jpg”,
    “../img/profile-bg.jpg”,
    “../img/cu-logo-svg.png”,
    “https://creatorup.com/wp-content/uploads/2015/05/YouTube-Production-Skills-Boot-Camp.png”,
    “https://creatorup.com/wp-content/uploads/2015/01/Storytellling.png”,
    “https://creatorup.com/wp-content/uploads/2015/01/Film.png”,
    “https://creatorup.com/wp-content/uploads/2015/01/WebSeries.png”,
    “https://creatorup.com/wp-content/uploads/2015/01/Crowdfunding.png”,
    “https://creatorup.com/wp-content/uploads/2015/01/BrandedContent.png”
    ]).then(function(){
    console.log(“Images done loading!”);
    },function(failed){
    console.log(“An image filed: ” + failed);
    });

    the local images in iOs all return “Failed to load resource: The requested URL was not found on this server.” It works fine on browser but it can not find the images when emulate on iOs. The images loaded from Internet with http protocol seems to work fine. Why and how to fix that?

    • It probably does not work on the device due to the ../ at the start of the URL. Try just img/signin.jpg for example.

      • Oh thank you! The code actually look directly under www folder, not from where app.js file is (which is under js folder). Other question – how do I know the code is actually working? Like when the loaded images are needed in a view, is there console log message to tell me the images name are used? Or any success message beside the initial load?

  11. Hi Andrew,
    many thanks for your works. Did you noticed problems with the update to iOS9?
    Thanks

    • I know there were a lot of talk from the Ionic team (and a blog post or 2 I believe) regarding iOS9 issues.

      • Hi, yes. Thanks to the Patch of the angular team the Apps work properly. After a fast review Your directive seems to not work like before. In any case, many thanks

  12. is there a method for pre caching short mp4 files?

    • Good news and bad news. The HTML5 video tag has a preload attribute that may help a bit here. Problem is support is… well, I’m not even sure. I can’t seem to find a reliable source of what browsers support it on mobile. Caniuse doesn’t have it at all and MDN has a lot of question marks.

      • hmmm ya, i think some undesired hacking with canplaythrough or preload might be necessary…?

  13. This tutorial works great, thank you for that!! But how do I check if the image is already cached??
    I wanna load a image right after the page loads, n it is successfully gets cached. But when offline that same function gets called, so it fails to load, so is there any way to check if the image is cached or not????

    • It’s complicated. 😛

      So, you can check if an image is cached using something similar to the following:

      function is_cached(src) {
          var image = new Image();
          image.src = src;
      
          return image.complete;
      }
      

      The problem is, if it is NOT cached, it loads the image, so it is technically doing more than expected. I am hesitant to add it to the module for this reason.

  14. How to call cached image in my view, just use normal img src=”‘ or anything else to add ?

  15. madfan_en4ik

    Thanks, Andrew, this tutorial is great, and do you have any idea about caching audio?

Leave a Reply