Image.complete is true after onload, but after onload second image not anymore - javascript

I'm using Javascript to load 2 images and drawing them on a canvas.
I use the onload event of an image to drawn the images after they are both loaded.
var loadedImagesCount = 0;
var NUM_OF_TILES=2;
window.onload=function(){
background.onload = imageOnLoad();
background.src ="background.png";
layer1 = document.getElementById("layer1");
ctx1 = layer1.getContext("2d");
character.onload = imageOnLoad();
character.src ="character.png";
layer2 = document.getElementById("layer2");
ctx2 = layer2.getContext("2d");
}
function imageOnLoad(){
loadedImagesCount++;
window.alert(''+loadedImagesCount+ ' ' + background.complete + ' ' + character.complete);
if (loadedImagesCount==NUM_OF_TILES) drawAll();
}
The imageOnLoad() is called twice (as expected). The first time is says (through an alert) that both images are loaded. The second time it says that the character image is loaded, but the background image not anymore? How is this possible? Why would it unload the image again? After a refresh this does not happen anymore, it only happens the first time you load the page.
And because the background is not loaded, when the canvas is drawn, only the character is being drawn.

This maybe because you have not declared background as a global variable outside of the window.onload function. So background is not the variable you think it is.
Check this code that works as expected and is derived from yours and notice how background and characters are "vared" outside of the window.onload function :
<html>
<script language="javascript">
var loadedImagesCount = 0;
var NUM_OF_TILES=2;
var background;
var character;
window.onload=function(){
background = document.getElementById("background");
character = document.getElementById("character");
background.onload = imageOnLoad();
background.src ="HSHOP.jpg";
character.onload = imageOnLoad();
character.src ="HSHOP 2.jpg";
}
function imageOnLoad(){
loadedImagesCount++;
window.alert(''+loadedImagesCount+ ' ' + background.completes + ' ' + character.complete);
//if (loadedImagesCount==NUM_OF_TILES) drawAll();
}
</script>
<body>
<img id="background"/>
<img id="character"/>
</body>
</html>
Quick note : As a general rule try to clean up your code as much as possible before posting it so that it is easier for us to find what's going wrong.

Problem solved. The onload variable needs a method without the parentheses, if you use the parentheses it will call the method immediately.

Related

ajax undefined after a thousand iterations

When creating an animation I try to use javascript for additional effects, namely snow being piled up and falling off the edges of the foreground. I figured that the javascript could do the "calculations" on a <canvas> that had the image, then send the "snow" to a php script that would create the png images. This is all placed on my local "server" because that is the only one that can write files.
<html>
<head>
<title>Making Snow</title>
</head>
<body bgcolor="black">
<canvas id="canvas" width="1920px" height="1080px"></canvas>
</body>
<script src="http://ajax.googleapis.com/ajax/libs/jquery/1.11.0/jquery.min.js"></script>
<script>
var canvas;
var ctx;
var frame=-530;
var simg = new Image()
var dimg = new Image()
onload = function()
{
canvas = document.getElementById("canvas");
ctx = canvas.getContext("2d");
simg.src = "prodB/C/DGB.0530.png"
}
simg.onload = function()
{
var ipo=3;
// Initialize all pixels on the screen/page
ctx.clearRect(0,0,canvas.width,canvas.height);
ctx.drawImage(simg,0,0);
document.title = "Making Snow " + (frame+530) + " / 7000";
snowdraw();
}
dimg.onerror = function()
{
dimg.src = "../imagewriting/snow" + zeroFill(frame+530,4) + ".png";
}
dimg.onload = function()
{
frame++;
if(frame<530)
simg.src = "prodB/C/DGB.0530.png"
else if(frame>4400)
simg.src = "prodB/C/DGB.4400.png"
else
simg.src = "prodB/C/DGB." + zeroFill(frame,4) + ".png"
}
var snowdraw = function()
{
var temp;
var canvasData = "";
// console.log(screen);
// Animation
// Choose a random pixel at the top of the screen
if(frame<7000)
{
for(ny=canvas.height-2; ny>=0; ny--)
{ // Iterate through all the pixels starting from the bottom right corner.
for(nx=canvas.width-2; nx>=0; nx--)
{
canvasData=canvasData+"1";
}
}
$.ajax({
method: "POST",
url: "makesnow.php",
data:{ imgData: canvasData, frame: (frame+530) }
})
.done(function( msg ) {
dimg.src = "../imagewriting/snow" + zeroFill(frame+530,4) + ".png";
});
}
else
{
document.title = "COMPLETE";
}
}
// http://stackoverflow.com/questions/1267283/how-can-i-create-a-zerofilled-value-using-javascript
// by Peter Bailey http://stackoverflow.com/users/8815
function zeroFill( number, width )
{
width -= number.toString().length;
if ( width > 0 )
{
return new Array( width + (/\./.test( number ) ? 2 : 1) ).join( '0' ) + number;
}
return number + ""; // always return a string
}
</script>
</html>
However, on the 1640th frame (or more precisely frame=1110) ajax is suddenly undefined. The image snow1640.png is created, but the browser tells me ajax is not defined and won't advance passed Making Snow 1640 / 7000. Because of the small random nature for each "snow flake" I can't just pick it up from where I left off, as the snow would jump from one frame to the next. Though I did try that at one point and ajax still stopped after that frame.
I first ran it on the local machine running Firefox (http://127.0.0.1/...) then moved onto another machine on the network which is more powerful running Chrome and both died on that same frame. Thought it might be a memory or file limit so I moved the complete frames out of there. Still nothing.
EDIT: Code now snippit of just the problem.
Also, console.log for data and ajax.responseText seem to be generally empty, both during successful "renders" and when it starts iterating ajax is not defined (every other line empty, every other error).
EDIT: New development! Turns out that the error ReferenceError: ajax is not defined anyways gets called between frames (and the 404 when asking dimg if image was created).
EDIT: By typing in console.log($.ajax) after onload and commenting out simg.src I got function ajax().
Error persists, this time I expanded it to reveal the following:
success http://127.0.0.1/ag/makesnowBC.html 197:7
j http://ajax.googleapis.com/ajax/libs/jquery/1.11.0/jquery.min.js 2:27131
fireWith http://ajax.googleapis.com/ajax/libs/jquery/1.11.0/jquery.min.js 2:27949
x http://ajax.googleapis.com/ajax/libs/jquery/1.11.0/jquery.min.js 4:22242
b http://ajax.googleapis.com/ajax/libs/jquery/1.11.0/jquery.min.js 4:26298
EDIT: Changed the code to now use Synchronous ajax. This time there are no error messages what so ever, but it still stops at 1640.

Why JavaScript code execute second part sometimes

I am draw background on canvas and also small images on that background. But sometimes, background draw on small images. Why ?
JavaScript code -
var canvasupdate = document.getElementById("myCanvas");
ctxupdate = canvasupdate.getContext("2d");
var background = new Image();
background.src = sitePath + "ATOM/chapter1/book/" + `bgimagename`;
background.onload = function() {
ctxupdate.drawImage(background, 0, 0); // set background image
};
var imageobj = new Array();
for (var d = 0; d < calloutImageArray.length; d++) // getting small images in array
{
imageobj[d] = new Image();
(function(d) {
imageobj[d].src = sitePath + "ATOM/chapter1/book/" + calloutImageArray[d];
imageobj[d].onload = function() {
ctxupdate.drawImage(imageobj[d], calloutImageArrayX[d], calloutImageArrayY[d], calloutImageArrayW[d], calloutImageArrayH[d]);
};
})(d);
}
In above code, background image code should be execute first then call out image(small image) code execute but some time execute small images code and then background image code why?
All the images are loaded asynchronously. So sometimes the small images are loaded (and drawn) before the background image. Please take a look into e.g. Network tab in Chrome in which order the resources load is done.
The simplest solution for this problem is to move the loading of small images into the callback function for load event of the background image.
The onLoad function runs asynchronously. That means JavaScript will continue to run the rest of your code and will run the callback function when the background image is loaded. That's why your second part of the code runs first. So instead try to add your code inside the onload function like this:
var canvasupdate = document.getElementById("myCanvas");
ctxupdate = canvasupdate.getContext("2d");
var background = new Image();
background.src = sitePath + "ATOM/chapter1/book/" + `bgimagename`;
background.onload = function() {
ctxupdate.drawImage(background, 0, 0); // set background image
var imageobj = new Array();
for (var d = 0; d < calloutImageArray.length; d++) // getting small images in array
{
imageobj[d] = new Image();
(function(d) {
imageobj[d].src = sitePath + "ATOM/chapter1/book/" + calloutImageArray[d];
imageobj[d].onload = function() {
ctxupdate.drawImage(imageobj[d], calloutImageArrayX[d], calloutImageArrayY[d], calloutImageArrayW[d], calloutImageArrayH[d]);
};
})(d);
}
};
that way you can be sure that the background will be set first and then your small images. Note that I didn't try your code to check if it does what you want, I just rearranged the code blocks to show you the logic and why does not run as you would expect.
Hope it helps

JavaScript filter hueRotate does not work

I am trying to change an image's (#bgImage) hue when hovering over a button (profIcon) in JavaScript. This type of button is created with JS, and there are 9 of them.
Here is the code that creates a DOM image element, sets it's source, position, size and transition. Finally it appends it to it's container. The image appears correctly, where it should be.
I am using quickID in place of document.getElementById, and it is working without error.
var bgImage = document.createElement("img");
bgImage.id = "bgImage";
bgImage.src = "./resources/images/backgrounds/profession/selector.jpg";
bgImage.style.position = "absolute";
bgImage.style.left = "0px";
bgImage.style.top = "0px";
bgImage.style.width = "100%";
bgImage.style.height = "100%";
bgImage.style.zIndex = 1;
bgImage.style.webkitTransition = "all 0.5s ease";
quickID("centerdiv").appendChild(bgImage);
Here is the code that runs when I hover over an image:
profIcon.onmouseover = function () {
var thisNr = this.id.substr(8); //last char of the profIcon ID; number between 0 and 8
var newHue = profTomb[thisNr][3]; //this contains the value and only that.
console.log(newHue); //always returns the correct value
quickID(this.id).style.webkitFilter = "grayscale(0%)"; //this part works, too
quickID("bgImage").style.webkitFilter = "hueRotate(" + newHue + "deg)";
}
My problem: for some reason, the filter does not apply. newHue is either a positive (75), or a negative (-23) value and it's inserted correctly, as it appears in the console log. I only use webkit vendor prefix as I use Google Chrome.
I waited up to 1 minute with my mouse cursor over the image, thinking my system needs time to process the transformation, but nothing happened.
Does anyone knows what is the problem?
The correct string to use is hue-rotate, not hueRotate. The following should work:
quickID("bgImage").style.webkitFilter = "hue-rotate(" + newHue + "deg)";

Get image dimensions with JavaScript using innerHTML

I'm working on an AJAX function that receives image URLs. However, I won't be using a node append to insert it. I'm using innerHTML, which makes it difficult to get the file dimensions.
At the moment, I'm using a function which is returning somewhat mixed results. Sometimes it gets the actual dimensions, other times it returns "0" as the image dimensions.
This is my function:
var url = "http://placekitten.com.s3.amazonaws.com/homepage-samples/408/287.jpg";
var width = getDimensions(url)[0];
var height = getDimensions(url)[1];
var insert = '<img src="'+url+'" width="'+width+'" height="'+height+'" />';
function getDimensions(path) {
var img = new Image();
img.src = path;
return [img.width, img.height];
}
Not sure why it's acting inconsistently though. Does anyone have any suggestions?
I was thinking it might be something to do with the AJAX inserting the image before it loads the dimensions, although not really sure.
Here's a fiddle, which seems to work as expected, but like I said, it's inconsistent.
http://jsfiddle.net/aH5re/1/
EDIT
Here is a second fiddle with a much larger image. I noticed it's a lot more inconsistent than a smaller image file
http://jsfiddle.net/aH5re/2/
You'll have to wait for the image to finish loading before you can get the dimensions properly and reliably. What's currently happening is that it's returning the dimensions before the image is potentially fully loaded. If you have it already cached you may be getting correct dimensions but on a large image uncached you're not going to get reliable results.
Have a look at this demo about how you could perhaps achieve that.
http://jsfiddle.net/robschmuecker/aH5re/5/
Javascript:
var url = "http://www.hdwallpapers.in/download/transformers_4_age_of_extinction-2560x1440.jpg";
var div = document.querySelector('div');
alert('loading image now');
var button = document.querySelector('.test');
getDimensions(url);
button.addEventListener('click', function () {
div.innerHTML = insert;
});
function getDimensions(path) {
var img = new Image();
img.src = path;
img.onload = function () {
alert('loaded');
var width = img.width;
var height = img.height;
insert = '<img src="' + url + '" width="' + width + '" height="' + height + '" />';
button.disabled = false
};
}

How to do a process after completion of another one in JavaScript

I want to add an image by Javascript, then calculating the html element width as
window.onload=function(){
document.getElementById('x').addEventListener('click', function(e){
var el = document.getElementById('xx');
el.innerHTML = '<img src="img.jpg" />';
var width = el.offsetWidth;
.....
}, false);
}
but since JavaScript conduct all processes simultaneously, I will get the width of the element before loading the image. How can I make sure that the image has been loaded into the content; then calculating the element width?
UPDATE: Thanks for the answers, but I think there is a misunderstanding. img src="img.jpg" /> does not exist in the DOM document. It will be added later by Javascript. Then, when trying to catch the element by Id, it is not there probably.
You can give the img an ID and do the following :-
var heavyImage = document.getElementById("my-img");//assuming your img ID is my-img
heavyImage.onload = function(){
//your code after image is fully loaded
}
window.onload=function(){
document.getElementById('x').addEventListener('click', function(e){
var el = document.getElementById('xx');
var img = new Image();//dynamically create image
img.src = "img.jpg";//set the src
img.alt = "alt";
el.appendChild(img);//append the image to the el
img.onload = function(){
var width = el.offsetWidth;
}
}, false);
}
This is untested, but if you add the image to the DOM, set an onload/load event-handler and then assign the src of the image, the event-handling should fire (once it's loaded) and allow you to find the width.
This is imperfect, though, since if the image is loaded from the browser's cache the onload/load event may not fire at all (particularly in Chromium/Chrome, I believe, though this is from memory of a bug that may, or may not, have since been fixed).
For the chrome bug you can use the following:-
var BLANK = 'data:image/gif;base64,R0lGODlhAQABAIAAAAAAAP///ywAAAAAAQABAAACAUwAOw==';//create a blank source
var tImg = document.getElementById("my-img");//get the image
var origSrc = tImg.src;//get the original src
tImg.src = BLANK;//change the img src to blank.
tImg.src = origSrc;//Change it back to original src. This will lead the chrome to load the image again.
tImg.onload= function(){
//your code after the image load
}
You can use a library called PreloadJS or you can try something like this:
//Somewhere in your document loading:
loadImage(yourImage, callbackOnComplete);
function loadImage(image, callbackOnComplete){
var self = this;
if(!image.complete)
window.content.setTimeout(
function() { self.loadImage(image, callbackOnComplete)}
,1000);
else callbackOnComplete();
}
I did this when I worked with images base64 which delay on loading.

Categories

Resources