Chrome thinks images aren't loades while they are? - javascript

I'm trying to draw a few images on a canvas, to make sure the images are loaded when I draw them, I preload them using an array.
var num = 0,
length = 3,
images = {
img1: '/img/img1.png',
img2: '/img/img2.png',
img3: '/img/img3.png'
}
for(var i in images)
{
var tmp = new Image();
tmp.src = images[i];
tmp.onload = function()
{
num ++;
if(num == length)
{
startDoingStuff();
}
}
}
After preloading the startDoingStuff is fired where I use the image again
<snip>
this.img = new Image();
img.src = images[id].src;
</snip>
This works in Firefox and Opera but not in Chrome. Chrome probably thinks the images aren't loaded because if I put yet another onload it will work. Which is stupid because the images were already loaded :/
Only thing I found to solve this is by putting the loaded images in another array (or overwriting the original array) thus keeping reference.
Why doesn't Chrome not realize he already has the images in cache and use them when asked for?

Your code to preload the images are not very correct. Try next function:
function preloadImages(images,callback){
var img,imgs,finished=0,onLoadError;
if(images instanceof Array)imgs=images;
else{
imgs=[];
for(var p in images)if(images.hasOwnProperty(p))imgs.push(images[p]);
}
onLoadError=function(ev){
//ev=ev||window.event;
// ev.type can be used to check
// when image is loaded succ (ev.type==="load")
// or fail (ev.type==="error")
finished++;
if(finished===imgs.length)callback();
};
for(var i=0;i<imgs.length;i++){
img=new Image();
img.onload=img.onerror=onLoadError;
img.src=imgs[i]; // NOTE: .src must be assigned after .onload and .onerror!
}
}
You can use this function like in next examples:
preloadImages({ img1: '/img/img1.png',
img2: '/img/img2.png',
img3: '/img/img3.png' },
startDoingStuff);
// or
preloadImages([ '/img/img1.png',
'/img/img2.png',
'/img/img3.png' ],
startDoingStuff);

Related

javascript load and show each image in loop

I'm new to javascript, and I've been stuck on this simple problem for days. I have a series of thousands of images (collectively a "dataset") that when viewed rapidly one after the other gives the appearance of video. I want to loop through all of my images.
I've created a function like so:
function updateImage(dataset, record_id) {
image_url = '/image?dataset='+dataset+'&record-id='+record_id;
if ($('#mpeg-image').length > 0) {
$('#mpeg-image')[0].src = image_url;
} else {
$("#image-thumbnail").html('<img id="mpeg-image", src="'+image_url+'"> </img>');
}
}
Calling the function once, e.g., updateImage('dataset_28_18-08-11',444); results in the image content updating in the browser. However, I want to show the images in a loop, so I created a function like this:
function playVideo() {
for (i = 0; i < 1800; i++) {
updateImage(dataset,i);
}
}
This function uses the same updateImage() function that worked above, but when I run playVideo(); Chrome doesn't update the image until the very end. The src tag changes, but the actual content does not. How can I alter my loop so that each image is loaded and shown in sequence? I don't want to use something like "Sprite" that merges all images into one, since that's too data intensive on my backend. I just want to load and show my images one after the other.
Browsers won't re-render page elements until there's a break in the execution of your code. Your entire image loop will run before the browser ever gets a chance to re-paint the images.
It's not because the loop "runs too fast" or because the images haven't loaded (though that can definitely pose problems). To see an example of redrawing issue, try changing your animation into an infinite loop, where it continually plays. The page will freeze ("pinwheel") forever and the page elements will never be updated, even long after all of your images have loaded. You need to give the browser an opportunity to redraw the page whenever you've made changes to page elements.
In web animation, this is often done via window.requestAnimationFrame() (see MDN docs). The method takes a callback function which is executed after the browser has redrawn the page. The browser will execute your callback once it's ready for page changes.
A simplistic example of this with you code would go something like this.
var imageNo = 0;
function step() {
updateImage(dataset, imageNo);
imageNo++;
if (imageNo < 1800) {
window.requestAnimationFrame(step);
}
}
window.requestAnimationFrame(step);
You can achieve a similar effect using setTimeout() or setInterval(), but those methods aren't optimized for animation.
The reason why you only see the last image is because of two things.
1. as #andriusain said, it takes the browser some indeterminate amount of time to download each image.
2. The loop runs so fast that the last iteration is what takes affect.
With that said, it would be best to either sprite the image (which it sounds like you can't do), or you can preload the images so that the browser caches them, then set the urls. You can do something like this.
// using es6
// I'd recommend adding <img src="mpeg-image" /> to your html page
// just to simplify the code. Also I recommend saving the image
// element in a local variable instead of using jQuery in each
// iteration loop which will definitely give a slower performance.
const mpegImgElement = $('#mpeg-image')[0];
const FPS = 30 / 1000; // 30 Frames Per Second
function playVideo() {
preLoadImages()
.then(urls => {
playUrls(urls);
})
.catch(error => {
console.log('error loading images', error);
})
}
function playUrls(urls) {
const index = 0;
const intervalId = setInterval(() => {
// set the url
mpegImgElement.src = urls[index];
// all done. stop the interval
if (++index === urls.length) {
clearInterval(intervalId);
}
}, FPS);
}
function preLoadImages() {
const promises = [];
for (i = 0; i < 1800; i++) {
const imageUrl = '/image?dataset='+dataset+'&record-id='+record_id;
const promise = loadImage(imageUrl);
promises.push(promise);
}
return Promise.all(promises);
}
function loadImage(url) {
return new Promise((resolve, reject) => {
const image = new Image();
image.addEventListener('load', () => {
resolve(url);
});
image.addEventListener('error', error => {
reject(error);
});
image.src = url;
});
}
This happens because the browser is still loading the images... I think your best bet is to programmatically load them first and once they are loaded then play the video... to do that maybe something like this will help:
var loadImages = function(images, callback) {
var loadTotal = 0;
for (var i = 0; i < images.length; i++) {
var image = new Image();
image.addEventListener('load', function() {
loadTotal++;
if (loadTotal === images.length) {
callback();
}
});
image.src = images[i];
}
}
var playVideo = function() {
// do stuff...
}
var images = ['image1.jpg', 'image2.jpg'];
loadImages(images, playVideo);
This idea is terrible.
JS is executed in single thread, while calling data from src is asynchronous method. Calling one by one and wait then definitely block the thread.
In this case you need promise to prepare all images first.
I make a workable example using google logo.
paths = ["https://www.google.com.hk/images/branding/googlelogo/2x/googlelogo_color_272x92dp.png",
"https://www.google.com.hk/images/branding/googlelogo/2x/googlelogo_color_272x92dp.png",
"https://www.google.com.hk/images/branding/googlelogo/2x/googlelogo_color_272x92dp.png"]
var imgCount = paths.length;
function loadImage(url) {
return new Promise((resolve, reject) => {
let img = new Image();
img.addEventListener('load', e => resolve(img));
img.src = url;
});
}
promises = []
for (var i = 0; i < paths.length; i++){
path = paths[i];
promises.push(loadImage(path));
}
Promise.all(promises).then(imgs => {
imgs.forEach(e => document.getElementById('image-holder').appendChild(e));});
//What I done here is display then all, but you can make it like, replace one by one, or declare your own display function.
<div id="image-holder"></div>

(Image in loop) Huge array of images and wants to display if image is really present then show the images otherwise delete/hide the image node

I have a huge array of images
Actually I am bit worry about checking the image that really exist and also want do something if image present
and if not found I am doing some other work.
Note: If one image is checking on server and takes 3 minutes
then how to handle in loop for other images.
I am just found below image if image not exist :-
Please give a standard way that can be used for nth number of images in array.
var urlArray = [{productImage:'http://www.queness.com/resources/images/png/appleeeeeee_ex.png'},{productImage:'https://www.google.co.in/images/branding/googlelogo/1x/googlelogo_color_272x92dp.png'},{productImage:'https://www.google.co.in/images/branding/googlelogo/1x/googlelogo_cooooooolor_272x92dp.png'},{productImage:'https://www.google.co.in/images/branding/googlelogo/1x/googlelogo_color_272x92dp.png'}];
<div>
<img src=""> // actually this comes through urlArray
</div>
Thanks in advance!
If you want to check image URL exist or not, You can try this with javaScript,
It will check all URLs one by one and call the callBack function, then it will call URL check for next URL.
Next URL will be check after one completes, no matter how much time it will take.
function isImageExists(imgSrc, callBackAfterCheck) {
var img = new Image();
img.onload = function() { callBackAfterCheck(true) };
img.onerror = function() {callBackAfterCheck(false) };
img.src = imgSrc;
}
var urlArray = [{productImage:'http://www.queness.com/resources/images/png/appleeeeeee_ex.png'},{productImage:'https://www.google.co.in/images/branding/googlelogo/1x/googlelogo_color_272x92dp.png'},{productImage:'https://www.google.co.in/images/branding/googlelogo/1x/googlelogo_cooooooolor_272x92dp.png'},{productImage:'https://www.google.co.in/images/branding/googlelogo/1x/googlelogo_color_272x92dp.png'}];
var imageIndex = 0;
function callBackAfterCheck(status){
if(status == true){
//do your stuff here
console.log('image Exist');
}else{
//do your stuff here
console.log('image not Exist');
}
imageIndex++
if(imageIndex < urlArray.length){
var imageUrl = urlArray[imageIndex].productImage;
isImageExists(imageUrl, callBackAfterCheck)
}
}
var imageUrl = urlArray[imageIndex].productImage;
isImageExists(imageUrl, callBackAfterCheck);
Here you can pass your image source and the function you want to execute after checking the source.
It will check your image existence and you can get the status (true or false) in your callBack function,
Now you can do your stuff according to your need in callback function.

How to know when browser finish to process an image after loading it?

When an image object is created, can know when is fully loaded using the "complete" property, or the "onload" method, then, this image has processed ( resizing for example ) using some time, that can be some seconds in big files.
How to know when browser finish to process an image after loading it?
EDIT: In examples can see a lag between "complete" message and the appearance of the image, I want avoid this.
Example ussing "onload" method:
var BIGimage;
putBIGimage();
function putBIGimage(){
BIGimage=document.createElement("IMG");
BIGimage.height=200;
BIGimage.src="http://orig09.deviantart.net/5e53/f/2013/347/f/d/i_don_t_want_to_ever_leave_the_lake_district_by_martinkantauskas-d6xrdch.jpg";
BIGimage.onload=function(){waitBIGimage();};
}
function waitBIGimage(){
console.log("Complete.");
document.body.appendChild(BIGimage);
}
Example using "complete" property:
var BIGimage;
putBIGimage();
function putBIGimage(){
BIGimage=document.createElement("IMG");
BIGimage.height=200;
BIGimage.src="http://orig09.deviantart.net/5e53/f/2013/347/f/d/i_don_t_want_to_ever_leave_the_lake_district_by_martinkantauskas-d6xrdch.jpg";
waitBIGimage();
}
function waitBIGimage(){
if (!BIGimage.complete){
console.log("Loading...");
setTimeout(function(){
waitBIGimage();
},16);
} else {
console.log("Complete.");
document.body.appendChild(BIGimage);
}
}
EDIT: Thanks the #Kaiido's response I made this sollution for wait the images process.
var imagesList=["https://omastewitkowski.files.wordpress.com/2013/07/howard-prairie-lake-oregon-omaste-witkowski-owfotografik-com-2-2.jpg",
"http://orig03.deviantart.net/7b8d/f/2015/289/0/f/0ffd635880709fb39c2b69f782de9663-d9d9w6l.jpg",
"http://www.livandiz.com/dpr/Crater%20Lake%20Pano%2016799x5507.JPG"];
var BIGimages=loadImages(imagesList);
onLoadImages(BIGimages,showImages);
function loadImages(listImages){
var image;
var list=[];
for (var i=0;i<listImages.length;i++){
image=document.createElement("IMG");
image.height=200;
image.src=listImages[i]+"?"+Math.random();
list.push(image);
}
return list;
}
function showImages(){
loading.style.display="none";
for (var i=0; i<BIGimages.length;i++){
document.body.appendChild(BIGimages[i]);
}
};
function onLoadImages(images,callBack,n) {
if (images==undefined) return null;
if (callBack==undefined) callBack=function(){};
else if (typeof callBack!="function") return null;
var list=[];
if (!Array.isArray(images)){
if (typeof images =="string") images=document.getElementById(images);
if (!images || images.tagName!="IMG") return null;
list.push(images);
} else list=images;
if (n==undefined || n<0 || n>=list.length) n=0;
for (var i=n; i<list.length; i++){
if (!list[i].complete){
setTimeout(function(){onLoadImages(images,callBack,i);},16);
return false;
}
var ctx = document.createElement('canvas').getContext('2d');
ctx.drawImage(list[i], 0, 0);
}
callBack();
return true;
}
<DIV id="loading">Loading some big images...</DIV>
The HTMLImageElement interface has a decode() method, which does allow us to wait until the image is ready to be drawn, just like you want.
var BIGimage;
putBIGimage();
function putBIGimage() {
BIGimage = document.createElement("IMG");
BIGimage.height = 200;
BIGimage.src = "https://upload.wikimedia.org/wikipedia/commons/c/cf/Black_hole_-_Messier_87.jpg?r=" + Math.random();
BIGimage.onload = e => console.log('load event', performance.now());
BIGimage.decode().then(waitBIGimage);
BIGimage.onerror = console.error;
}
function waitBIGimage() {
// uses the synchronous testing method to check it works fine
var start = performance.now();
// only to see if it worked fine
var ctx = document.createElement('canvas').getContext('2d');
ctx.drawImage(BIGimage, 0, 0);
ctx.drawImage(ctx.canvas, 0, 0);
var end = performance.now();
console.log(`it took ${end - start}ms to draw`)
// do your stuff
document.body.appendChild(BIGimage);
}
Another way, is to use the fact that the CanvasContext2D drawImage method can be synchronous. Some browsers may try to delay the actual painting for the next painting frame, but by drawing the canvas onto itself we can force most of the current UAs to render our image synchronously.
So you can use it as a waiting method in your waitBIGimage method.
var BIGimage;
putBIGimage();
function putBIGimage() {
BIGimage = document.createElement("IMG");
BIGimage.height = 200;
BIGimage.src = "https://upload.wikimedia.org/wikipedia/commons/c/cf/Black_hole_-_Messier_87.jpg?r=" + Math.random();
BIGimage.onload = waitBIGimage;
BIGimage.onerror = console.error;
}
function waitBIGimage() {
// only for demo
// we've got to also log the time since even the console.log method will be blocked during processing
var start = performance.now();
console.log('waiting', start);
// this is all needed
var ctx = document.createElement('canvas').getContext('2d');
ctx.drawImage(BIGimage, 0, 0);
// on some system the drawing of the image may be delayed to the next painting frame
// we can try force the synchronous rendering by drawing the canvas over itself
ctx.drawImage(ctx.canvas, 0, 0);
// demo only
var end = performance.now();
console.log("Complete.", end);
console.log(`it took ${end - start}ms`)
// do your stuff
document.body.appendChild(BIGimage);
}
On my Firefox it takes about 1 second to process the image, while on my Chrome, it's a bit faster.
But one big issue with method is that it is synchronous, and thus will block your scripts during all the time the Image is processed.
Yet another way, is to use the createImageBitmap() method, which should allocate the image bitmap in the GPU, ready to be painted.
There is no reliable way to know - your browser can continue to execute arbitrary javascript or perform built in functions (i.e., resizing of the browser window) which can directly or indirectly affect the image and cause it to be either redrawn, or appear to not finish drawing for a while.
You can hook in to particular events in the lifetime of an image, but, strictly speaking, "finish" only happens when the browser window is closed.

If an image image path exists replace the text, if not display original image [duplicate]

This question already has answers here:
Load image if found, else load another image
(4 answers)
Closed 8 years ago.
Hi I had set up a bit of code to replace images with their higher resolution versions. It looks like this
$("img[src*='.jpg']").each(function(){
$(this).attr('src',$(this).attr('src').replace('.jpg','.highres.jpg'));
});
Which just looks for all image and replaces the file name.
Sometimes though, the high res image is not given the file name of 'highres.jpg'
What I would like to do, is if highres.jpg exists, display that one. otherwise just display the original.
Here is what I tried:
$("img[src*='.jpg']").each(function(){
var replaceImage = function() {
$(this).attr('src',$(this).attr('src').replace('.jpg','.highres.jpg'));
};
if (replaceImage > -1) {
replaceImage;
};
});
Pretty sure I'm grasping at straws here and there's a better way to do this... cause it's not working :)
Here's a direct implementation of this answer:
$("img[src*='.jpg']").each(function(){
var imgSrc = $(this).attr('src'); // The original src
var highresSrc = imgSrc.replace('.jpg','.highres.jpg'); // High res src
var img = new Image();
img.onerror = function() { // In case of error, revert to original
img = new Image();
img.src = imgSrc;
};
/*
img.onload = function() {
// Image has loaded successfully. Do something if you want.
...
};
*/
img.src = highresImg; // Try loading highres
});

image.onload fires before image is completely loaded

I'm making a game using javascript + canvas.
I use the code below to ensure
var imgLoaded = 0;
var imgToLoad = multiImgs;
var onImgLoad = function()
{
imgLoaded++;
if(imgLoaded == imgToLoad)
{
ctx.drawImage()
}
}
for(var i = 0; i < multiImgs; i++)
{
images[i] = new Image();
images[i].onload = onImgLoad();
images[i].src = 'images/'+i+'.png';
}
This code at times works fine, esp. when the images are cached. However, when loading for the first time, at times, it gives INDEX_SIZE_ERR: DOM Exception 1
which I found is due to height & width of image not being available as suggested by Quickredfox in this answer... but then here drawImage is called only when all the images are loaded?
The error primarily occurs in mobile devices
You have to provide a reference to the load handler. Otherwise, the function executes immediately. Use:
images[i].onload = onImgLoad;

Categories