0

I have just upgraded a Meteor app to version 0.8, and a minor UX difficulty has surfaced.

I have a changeable image which is displayed like so:

<img src="/img/{{ image_name }}" class="pull-left">

The user can choose a new image (changing image_name, which is a field in a collection), and the image changes automatically on-screen, as it should.

However, in my tests it seems the behavior while the new image loads is different. In Meteor 0.7, the image went blank while the new one loaded. In Meteor 0.8, the old image stays on-screen until the new one is loaded. I can understand why this would usually be a better result, but in my case, the user is left wondering why the image didn't change, potentially for a few seconds.

So I would like to show a loading animation while the new image loads. I can start the animation when the new image is chosen, but how do I know when the image has finished loading, so that I can remove the animation?

Or have I misunderstood what is happening?

thanks!

Racing Tadpole
  • 4,270
  • 6
  • 37
  • 56

2 Answers2

2

You need to somehow use image load event. A possible solution would be to have two images, one presented to the user, second in a hidden div. The hidden one has a reactive source, just like you've got it right now. Visible one is static.

When user choose a new image:

  • Set the visible image source to a spinner (via jQuery),
  • Update the model so that the reactive image starts loading,

In the load callback for the hidden image, swap the source of visible image to the actual new source. See this question for note how to set up such callback.

Community
  • 1
  • 1
Hubert OG
  • 19,314
  • 7
  • 45
  • 73
1

Following @Hubert OG's advice, I solved it as follows.

In my template:

<div class="img-space">
    <div class="img-loading"></div>
    {{ triggerImageLoad }}
</div>

The img-loading class shows the spinner; I will use the triggerImageLoad call to replace it with the real image. (Though I don't like the idea of using such calls for their side effects - is there a better way?)

When a new image is chosen, I replace the image with the spinner again. In Meteor 0.8, such changes to the DOM are ok. So the javascript is:

function loadImage(cat) {
    if (cat) {
        var imageObj = new Image();
        imageObj.src = '/img/cat/'+cat.image_name;
        imageObj.addEventListener('load', function() { 
            $('.img-space').empty();
            $('.img-space').append($(imageObj));
        }, false);
    }
}

Template.myTemplate.triggerImageLoad = function() {
    var myCat = Cats.findOne(Session.get("cat_id"));
    loadImage(myCat);
}

Template.myTemplate.events(
    {'click .newImage' : function (evt, tmpl) {
            var catId = Session.get("cat_id");
            var myCat = Cats.findOne(catId);
            var new_image_name = evt.target.getAttribute('data-name');
            $('.img-space').empty();
            // don't like putting html in the js code - is there a better way?
            $('.img-space').append('<div class="img-loading"></div>');
            Cats.update(catId, {$set: {image_name: new_image_name}});
        }
    }
);

And finally, the css for a 24x24 pixel spinner and 200x200 pixel image is:

.img-loading {
    position: absolute; 
    top:88px; 
    left:88px; 
    width: 24px; 
    height: 24px;
    background-image: url('/img/site/img-loader.gif');
}

Hope that helps someone else.

Racing Tadpole
  • 4,270
  • 6
  • 37
  • 56