Swap an image without flicker while showing the new one immediately - javascript

TL;DR: Is there a way to swap the images reliably while showing whichever image is being loaded at the time without causing page flicker?
I have 2 images and 2 buttons and when I hover over one button it shows the one image. Hovering over the other button swaps to the second image. I was doing it like this:
$('#button1').mouseover(function() {
$('#image').attr('src', 'image1.png');
});
$('#button2').mouseover(function() {
$('#image').attr('src', 'image2.png');
});
This works fine but when the first image has loaded and the second hasn't, it doesn't show the second image until it has completed loading. To try to give the user some indication of when the new image is loading (which they're expecting to appear immediately), I forced it to add a null image before these swaps, like this:
$('#button1').mouseover(function() {
$('#image').attr('src', '#');
$('#image').attr('src', 'image1.png');
});
$('#button2').mouseover(function() {
$('#image').attr('src', '#');
$('#image').attr('src', 'image2.png');
});
This works great when one image is loading by showing the image as it's loading but now once both are loaded, the null image in between them causes a flicker when switching images. I thought I could fix this by turning the null image off once both images are loaded but that has turned out to be unreliable. Both $('#image').prop('complete') and imagesloaded as suggested in other locations on stackoverflow are inconsistent at noticing whether the image has been loaded or not. Detecting loaded images just seems to be a dead end.
I also considered trying to force the images to show and hide before and after they were created but this doesn't seem to work at all though I'm not sure why. The new one doesn't show while loading and I'm not sure if they're swapping properly:
$('#button1').mouseover(function() {
$('#image').hide();
$('#image').attr('src', 'image1.png');
$('#image').show();
});
$('#button2').mouseover(function() {
$('#image').hide();
$('#image').attr('src', 'image2.png');
$('#image').show();
});
Is there a way to swap the images reliably while showing whichever image is being loaded at the time without causing page flicker that I haven't tried?

What you're wanting to do is preload the images so that they are cached in the browser. then there's no delay on your mouse over. Here's a jquery plugin to cache the images and a call for them.
$.fn.preload = function() {
this.each(function(){
$('<img/>')[0].src = this;
});
}
// Usage:
$(['image1.png','image2.png']).preload();
This is not my code: credit to James # Preloading images with jQuery

Try storing the sources of the image into JavaScript variables, and use those variables to swap the image sources.
This might avoid the loading, not sure, but it might.

Try this:
var preloadImages = ['image1.png', 'image2.png'];
$('#button1').mouseover(function() {
$('#image').attr('src', preloadImages[0]);
});
$('#button2').mouseover(function() {
$('#image').attr('src', preloadImages[1]);
});

The problem is having this HTML content updated by changing the src location makes the browser wait until the image is loaded before displaying the image. This doesn't seem to be modifiable.
The HTML:
<img id="image" src="">
and javascript with this behavior:
$('#button1').mouseover(function() {
$('#image').attr('src', 'image1.png');
});
$('#button2').mouseover(function() {
$('#image').attr('src', 'image2.png');
});
Changing the HTML:
<div id='image_container'> </div>
and javascript to:
$('#button1').mouseover(function() {
// Remove the old image and replace it with the new one
$('#image_container').empty()
.append("<img id='image' src='image1.png'>");
});
$('#button2').mouseover(function() {
$('#image_container').empty()
.append("<img id='image' src='image2.png'>");
});
makes the browser show the image while it is downloaded. I'm not exactly sure why but it seems to just be that new <img>s are handled differently than <img>s with src modifications.

Related

Hiding an image until the document is loaded, then applying effects to it

I'm trying to find a simple, but bulletproof way of hiding an image until it's loaded, and then giving it some jQuery effects (e.g. fadeIn) but all the methods I've found seem to have some issues. For example, this solution:
<img id="photo" src="bigimage.jpg" style="display:none" />
$("#photo").load(function() {
$(this).fadeIn("slow");
});
May not trigger if the image is loaded before the DOM is ready (load() won't fire).
So I wrote the following, which only adds the URL once the image has definitely loaded...
$.fn.ImageLoad = function(url){
$image = $(this);
$("<img >").attr("src", url).load(function(){
$image.attr("src", url);
});
return $image;
};
$("#cast").ImageLoad("http://pas-wordpress-media.s3.amazonaws.com/wp-content/uploads/2013/08/Google-Office-Building-in-NYC.jpg").fadeIn("slow");
But it only runs the fadeIn("slow") once the image has been cached by the browser. The first time it loads, it just appears with no effects.
See your yourself here: http://jsfiddle.net/KKn4N/
Is there any way I can fix this?
Here is my version. Works every single time and completely cross browser.
$.fn.ImageLoad = function(url){
$this = $(this);
$this.hide().on('load', function(){
$this.fadeIn();
});
this[0].src = url;
};
The key is to
do the fade inside a callback
hide first
listen for the .load before you set the .src
DEMO
Yea, load the image first, then attach the fadeIn afterwards.
$.fn.ImageLoad = function(url){
$image = $(this);
$("<img >").attr("src", url).load(function(){
$image.attr("src", url);
});
return $image;
};
$("#cast").ImageLoad("http://www.wired.com/wiredenterprise/wp-content/uploads/2013/07/ff_googleinfrastructure_large.jpg");
$("#cast").fadeIn("slow");
Try this
<img
id="cast"
style="display:none"
src="http://pas-wordpress-media.s3.amazonaws.com/wp-content/uploads/2013/08/Google-Office-Building-in-NYC.jpg"
onload="$(document).ready(function(){$('#cast').fadeIn('slow')})"
/>
Not sure if I get it correctly - I'm assuming you mean hide all images when page loads until all images are loaded and then fade them in?
If so, then why not just set display:none on images (maybe give them a class .hidden {display:none}) and then wait for window load event (i.e. all loaded) and trigger fade on all images - they will then nicely all fade in at the same time.
$('img.hidden').fadeIn('slow');
Example:
html
<img class=hidden src=http://cdn-4.lifehack.org/wp-content/files/2013/09/javascript-logo.png></img>
css
.hidden{
display:none;
}
js
$(window).load(function(){
$('img.hidden').fadeIn('slow');
});

jQuery load() appears to get stuck/hang

I am using jQuery load() to check if an image that I'm replacing the src on is loaded. Sometimes it appears to get stuck or hang though. I have a jsFiddle link below to check out. To make the loading graphic get stuck on the page click one of the buttons twice.
http://jsfiddle.net/dmcgrew/LLMs8/3/
This "almost" replicates a problem on a site I'm currently building, but I think the issues are related. On the site I'm building this "hanging" only happens when I do the following steps:
Click Image 3 button
Click Hide button
Click Image 3 button again
The loading graphic is now stuck on the page.
Here is my JS...
$("button").not("off").bind("click", function(){
var imgPath = $(this).attr("data-image"); //grab image path from data attr
console.log(imgPath);
$(".loading").show(); //show loading gif
$("img.the_image").hide(); //hide the img
$("img.the_image").attr("src","http://farm7.staticflickr.com/"+imgPath).load(function() {
$(".loading").hide(); //hide loading gif
$("img.the_image").show(); //show the newly loaded img
});
});
$("button.off").bind("click", function(){
$("img").hide();
});
Is load() the best way to check if an image has been loaded? Is there a better way that I should replace the image and check if its loaded (maybe AJAX?).
You have two issues: First, your code attaches the load handler multiple times, which is causing funky behavior. Second, your code doesn't handle multiple clicks on the same element in a row. Try this:
http://jsfiddle.net/E3Avx/
$("button").not("off").bind("click", function () {
var imgPath = $(this).attr("data-image"); //grab image path from data attr
var newImgPath = 'http://farm7.staticflickr.com/' + imgPath;
if ($('img.the_image').attr('src') != newImgPath) {
$(".loading").show(); //show loading gif
$("img.the_image").hide(); //hide the img
$("img.the_image").attr("src", newImgPath);
}
});
$("img.the_image").load(function () {
console.log('load handler');
$(".loading").hide(); //hide loading gif
$("img.the_image").show(); //show the newly loaded img
});
$("button.off").bind("click", function () {
$("img").hide();
});
This problem seems to be related to you trying to load() the same image twice, as the src doesn't actually change I think it's causing problems.
The easiest way to deal with it is just to check if the current src matches the src that has been selected, eg:
if($('img.the_image').attr('src') != 'http://farm7.staticflickr.com/' + imgPath) { }
http://jsfiddle.net/LLMs8/7/

jCrop (jQuery) sometimes fails to load image/cropper area

I've got a pretty simple problem, but I've become clueless on what is causing the problem. In one of my applications I'm using jCrop as a small add-on to crop images to fit in banners/headers etc. These steps will be taken:
1) Select an image (using CKFinder for this, CKFinder returns the image path to an input field)
2) Click a button to load the image
3) Crop the image
4) Save the image
in about 75% of the cases everything goes according to plan, however the in the other 25% of the cases jCrop fails to load the cropping area and leaves it blank. Here's the jQuery code I'm using:
jQuery('#selectimg').live('click', function(e) {
e.preventDefault();
var newsrc = jQuery('#img2').val();
jQuery('#cropbox').attr('src', newsrc);
var jcrop_api = jQuery.Jcrop('#cropbox', {
boxWidth: 700,
boxHeight: 700,
onSelect: updateCoords,
onChange: updateCoords
});
//Some other JS code come's here for buttons (they work all the time)
});
I noticed that when I left the part away where #cropbox is being transformd in a cropable area, that the image is loading just fine, so the mistake lies with the var = jcrop_api part, but I slowsly start to think that there is no solution for this...
This is what I've tried so far:
Making a div <div id="cropper-box"></div> and use jQuery('#cropper-box').append('<img src="" id="cropbox" />'); and afterwards set the value. I tried the same thing but setting the image src in 1 step instead of afterwards.
I tried to put a placeholder on the page <img src="placeholder.png" id="cropbox" /> and change the source upon clicking the button. This works, but the cropperarea stays the size of the image (300x180px or something) and doesn't get bigger as it should.
// Edit:
Trying some more showed me that the image source is being replaced properly(! using Firefox to show the source for the selected text), I double checked the URL but this was a correct URL and a working image.
At the place where the cropper should be, there's an about 10x10 pixel white spot where the cropper icon (a plus sign) is popping up.. but as said before: the image isn't shown.
// Edit 2:
So I've took the sources for both the 1st and the 2nd try for the same image. As told before the first try the image won't load properly and the 2nd try it does (only when the 2nd try is the same image(!!)).
The selected page source shows 1 difference which is, first try:
<img style="position: absolute; width: 0px; height: 0px;" src="http://95.142.175.17/uploads/files/Desert.jpg">
second try:
<img style="position: absolute; width: 700px; height: 525px;" src="http://95.142.175.17/uploads/files/Desert.jpg">
I guess this is the image that's being replace by jCrop, but it's a complete riddle why it puts 0 heigth/width in there the first and the proper sizes the second time.
Okay guys, in case anyone else runs into this problem:
jCrop kinda gets messed up if the actions of loading an image and applying jCrop to it are queued too fast after eachother. I still find it strange that a second attempt works perfect, but I think that has something to do with cached image dimensions which are recognized by the DOM of the page or something.
The solution I came up with was by creating a function that converts the #cropbox into a jCrop area and then setting a 2 second interval, just to give jCrop some time to recognize the image and it's dimensions and then convert the element.
This is the part of html I used (with a preloader):
<div id="cropper-loading" style="display: none;"><img src="images/analytics/ajax-loader.gif" /></div>
<img id="cropbox" src="images/placeholder.png" style="display: none;" />
As you can see both the cropbox image and cropper-loading div are hidden as they are not needed instantly. You could display the placeholder if you wanted though.. Then this HTML form is used:
<input name="image2" id="img2" type="text" readonly="readonly" onclick="openKCFinder(this)" value="click here to select an image" style="width: 285px;" /> <button class="button button-blue" type="submit" name="load" id="selectimg">Load Image in cropper</button>
In my case I've been using KCFinder to load the images (it's part of CKEditor, really worth watching into!), KCFinder handles uploads, renaming etc and after choosing it returns the chosen image path (relative/absolute is configurable) to the input field.
Then when clicking #selectimg this code is called:
jQuery('#selectimg').click(function(e) {
e.preventDefault();
jQuery('#cropper-loading').css('display', 'block');
var newsrc = jQuery('#img2').val();
jQuery('#cropbox').attr('src', newsrc);
jQuery('#img').val(newsrc);
function createJcropArea() {
jQuery('#cropper-loading').css('display', 'none');
jQuery('#cropbox').css('display', 'block');
var jcrop_api = jQuery.Jcrop('#cropbox', {
boxWidth: 700,
boxHeight: 700,
onSelect: updateCoords,
onChange: updateCoords
});
clearInterval(interval);
}
var interval = setInterval(createJcropArea, 2000);
});
At first I prevent the link too be followed as it normally would (or button action) and after that the loading div is displayed (that's my reason for hiding the placeholder image, otherwise it would look messed up).
Then the image location is being loaded from the input field and copied into another (#img), this field is used to process the image afterwards (PHP uses the value of #img to load this image). Also simultaneously the #cropbox src is being set to the new image.
And here comes the part which solved my problem:
Instead of directly activating jCrop, I've made a function that:
1) hides the loading icon
2) displays the image
3) converts #cropbox into a jCrop area
4) clean the interval (otherwise it would loop un-ending)
And after this function you can see that, just to be save, I took 2 seconds delay before the jCrop area is being converted.
Hope it helps anyone in the future!
Cheers and thanks for thinking #vector and whoever else did ;-)
Creating an 'Image' object and setting up the 'src' attribute does not apply that you can treat the image like it had already been loaded.
Also, giving any fixed timeout interval does not guaranty the image has already been loaded.
Instead, you should set up an 'onload' callback for the Image Object - which will then initialize the Jcrop Object:
var src = 'https://example.com/imgs/someimgtocrop.jpg';
var tmpImg = new Image();
tmpImg.onload = function() {
//This is where you can safely create an image and a Jcrop Object
};
tmpImg.src = src; //Note that the 'src' attribute is only added to the Image Object after the 'onload' listener was defined
Try the edge library on the repo here: https://github.com/tapmodo/Jcrop
This should solve your problem. The lines that are changed to solve your problem:
// Fix size of crop image.
// Necessary when crop image is within a hidden element when page is loaded.
if ($origimg[0].width != 0 && $origimg[0].height != 0) {
// Obtain dimensions from contained img element.
$origimg.width($origimg[0].width);
$origimg.height($origimg[0].height);
} else {
// Obtain dimensions from temporary image in case the original is not loaded yet (e.g. IE 7.0).
var tempImage = new Image();
tempImage.src = $origimg[0].src;
$origimg.width(tempImage.width);
$origimg.height(tempImage.height);
}
Don't call this function onChange : updateCoords
Try it without and it will run smooth on mobiles.
You can create base64 directly and show them as an image wherever you want.
Here my weird but fantastic solution:
if (obj.tagName == 'IMG') {
var tempImage = new Image();
tempImage.src = $origimg[0].src;
$origimg.width(tempImage.width);
$origimg.height(tempImage.height);
if ($origimg[0].width > 1 && $origimg[0].height > 1) {
$origimg.width($origimg[0].width);
$origimg.height($origimg[0].height);
} else {
var tempImage = new Image();
tempImage.src = $origimg[0].src;
$origimg.width(tempImage.width);
$origimg.height(tempImage.height);
//console.log('error'+$origimg[0].width + $origimg[0].height);
}
I know this is old, but it was happening randomly to my install recently. Found that it was due to images not being full loaded before before jCrop intialized.
All it took to fix it was wrapping the jCrop initialization stuff inside of a
$(window).on("load", function () { //jcrop stuff here });
And it has been working well since.

Image load by jQuery

We have image and links in separate blocks.
When link is clicked, its href attribute puts to src of the image.
What I'm trying to do:
If image is not already loaded (not cached) {
fadeOut previous image {
fadeIn loader {
load image (when animation of loader ends) {
fadeOut loader {
fadeIn image
}
}
}
}
} else (if image is cached, do not show loader) {
fadeOut previous image {
fadeIn new image
}
}
Here is what I have: http://jsfiddle.net/EvXJr/13/
First part works, don't know how to code the second (else) part.
Please help.
The dirty way would be to assign ids to loaded images, if there is no element with such id on a page - show loader, else just animate image.
Example:
html:
<div id="container">
</div>
js:
$('#container').delegate('a', 'click', function () {
var sel_id = this.id;
if (!document.getElementById('img_' + this.id)) {
//create image with id like: sel_id = 'img_' + this.id
} else {
//just show image
}
});
Why don't you just keep track of what HREFs you have loaded with a javascript array that you have loaded and if your HREF can be found in the loaded array just take out the loader related functions.
Couldn't get the jsfiddle to load but if I understand correctly I would just set the background image of the element that holds the images to a loading gif then you don't have to worry about fading in and out a loader. If an image is already cached it will load so quickly you will never see the loading gif. And if it isn't and takes a while to load the user will see the loading gif and using the function below the image will fade in nicely once loaded.
And if you want to perform a function once an image has loaded use
$("img").load(function(){
//do stuff once image has loaded.
//for example
$(this).fadeIn("slow");
});

How do I display a gif animation to the user while a picture is loading (in the web)?

The problem:
I have set of pictures, when the user presses on one of them, it's grow to an area in the page.
The exchange of the pictures is done with the help of js.
Tthe picture is weigh about 0.5M, therefore it's take about 3 sec until the picture is showed.
I would like to present a type of animation while the picture is not displayed.
How can I do this with the help of js?
There's always the "marquee" tag with a "loading" message that you turn off as soon as your image is swapped in. Of course, even I would downvote anyone advocating marquee.
Use the load event, something like:
$("img.something").click(function() {
$(".someDiv").html('<img src="loading.gif"/>');
var $img = $('<img src="bigImage.jpeg" style="display:none"/>');
$img.load(function() {
// once the loading has completed
$(".someDiv").html($(this));
$(this).fadeIn("slow");
});
});
Insert a placeholder element and attach an onload event callback to the <img> element. With jQuery,
var imageElem = $('<img />'),
placeholder = $('<div class="loading">Loading...</div>');
imageElem.attr('src', 'http://example.com/big_image.jpg');
$('#images').append(placeholder).append(imageElem);
imageElem.hide().load(function() {
placeholder.remove();
imageElem.fadeIn();
});

Categories