I've read numerous posts and articles and tried both the JS-only onLoad method and using JQuery's .load(function() { way of going about things, but I can't find a good way to load an image quietly in the background and then do something once I know its actual width.
At this point I've got it all the way down as simple as I can get it, and it still returns "0":
bgImg = new Image();
bgImg.src = "./img/car-and-truck.gif";
bgImg.onload = imageLoaded();
function imageLoaded() {
alert(bgImg.width);
}
Use this instead:
bgImg.onload = imageLoaded;
You are calling imageLoaded immediately which you do not want to do.
Related
I'm working on a project in game development in Javascript and HTML5 canvas. I have this common code I use for loading sprites:
var sprite = new Image();
sprite.src = "sprite.png";
I was wondering if there was a simpler way to do this, which I first thought by function, but not sure how I should do so. I would think to do so like this:
function loadSprite(src) {
this.src = src;
}
var loadSprite(sprite.png);
However I don't think this is the right way to do it. Could someone correct my code and/or give a simpler way of loading an image like this? (I am also using a ctx.drawImage(..., sprite) in order to change coordinates on the canvas so it needs an x,y,width,and height parameters in one way or another)
Why not use as below:
function loadSprite(src) {
var sprite = new Image();
sprite.src = src;
return sprite
}
var _local_var = loadSprite('sprite.png');
I have some code that renders specific DOM elements to canvas, sort of like taking a screenshot. (It's custom code built for a very particular DOM structure as part of a graphics editing game, not a general library like rasterHTML.js)
The code flow is pretty procedural:
get some DOM elements of class A and draw them to canvas
get some DOM elements of class B and draw them to canvas
The trouble is that step 1 is very intensive compared to step 2, and doesn't finish drawing before step 2, screwing up the layers (in reality I have several canvases doing several things at once, and a canvas is unfortunately resized before all the drawing is completed). I've tried to replicate this in this fiddle: https://jsfiddle.net/1hucuLg9/
In pseudocode:
context.drawComplexSVG(); // slow
context.drawSimpleImage(); //fast
//canvas now has an SVG drawn on top of an image, not underneath.
I've seen a lot of setTimeout examples in an attempt to get one function to execute after another, but to me this seems to be a bit of a hack ... ideally I don't want to delay execution, just execute everything in strict order. I've also seen the idea of postMessage floated to achieve this but I've no idea how you pass messages to yourself. What's the correct way to ensure a function/line is fully executed (or in my case, the canvas is fully updated - is it the same thing?) before proceeding?
"getting some DOM elements" should be synchronous and do not require any complex code to handle sequencing draw operations.
The problem you are facing in your fiddle is that you are dynamically loading some images to draw - and for those, you need to wait, which makes the operation asynchronous.
Promises are here for your rescue, but you'll have to use them correctly. Just calling resolve right away like you did in your own answer will ensure some asynchrony, but that's not less fragile than a setTimeout approach. Instead, you should always create the promise at the heart of the asynchrony, in your case the image loading:
function loadImage(src) {
return new Promise(resolve, reject) {
var img = new Image();
img.onload = function(){ resolve(img); };
img.onerror = reject;
img.src = src;
});
}
so that you can use it in your canvas drawing code:
function drawSwatches(currentSwatch) {
var data = …;
var url = 'data:image/svg+xml; charset=utf8, ' + encodeURIComponent(data);
return loadImage(url).then(function(img) {
ctx.drawImage(img, 0, 0, vw, vh);
});
}
and then chain these properly:
var swatches = Array.from(document.getElementsByClassName("swatch"));
swatches.reduce(function(promise, swatch) {
return promise.then(function() {
return drawSwatches(swatch);
});
}, Promise.resolve()).then(function() {
var otherObjects = document.getElementsByClassName("otherObjects");
for (var i=0; i<otherObjects.length; i++) {
drawOtherObjects(otherObjects[i], 0, 0, 100, 100);
}
});
Well, it seems that this will get the job done:
var promise = new Promise(function(resolve){
context.drawComplexSVG();
resolve();
}
promise.then(function(){
context.drawSimpleImage();
}
If i had an image with a url such as img/160x180.jpg how using this alone in jquery/javascript can i get the width and height of it. i have tried
alert($('<img src="img/160x180.jpg"/>').naturalWidth)
in the example below it returns undefined.
http://jsfiddle.net/D7dx6/
Updated:
alert($('<img src="http://static.jquery.com/files/rocker/images/logo_jquery_215x53.gif"/>')[0].width);
edit — that doesn't really make sense; it needs to be in an event handler:
$('<img/>', {
'load': function() { alert(this.width); },
'src': 'http://static.jquery.com/files/rocker/images/logo_jquery_215x53.gif'
});
That code, it should be noted, might have problems on IE because it sometimes drops the ball if the "src" is set and the image is found in cache before the "load" handler is established. In that case, you can do this:
$('<img/>', { 'load': function() { alert(this.width); } }).prop('src', 'http://...');
There's no "naturalWidth" property.
Though it's pretty much irrelevant overhead in this case, you can do this without jQuery like this:
var img = new Image();
img.onload = function() {
alert(img.width);
};
img.src = "http://placekitten.com/300/400";
Now one thing to watch out for is that if you're looking at actual page elements (that is, <img> tags on the page), they might have "width" attributes that override the true size. Fetching the image again will generally pull it out of cache, though there's some potential pain there for huge images on mobile devices.
(edit — Graham points out that there is a "naturalWidth", but it's not well-supported at this time.)
naturalWidth and naturalHeight aren't well supported yet: see https://developer.mozilla.org/en/DOM/HTMLImageElement.
The pure-JavaScript way to determine the original dimensions of an image, whether or not it currently exists in the DOM, is to use the Image object.
var img = new Image();
img.src = 'path/to/img.jpg';
// you'll probably want to use setTimeout to wait until the imh.complete is true
console.log(img.width);
function loader(img) {
var imgH = img.height;
var imgW = img.width;
console.log(imgH, imgW);
};
img = new Image();
img.src ='../images/pic1.jpeg';
img.onLoad = loader(img);
So, It is exepeted, that I'll get image's size, but I got "0 0" in console. And size of image is 500X700. What's wrong with this code?
This makes no sense:
img.onLoad = loader(img);
you want to pass the actual function to the event:
img.onload = loader;
and use this instead of img within the function.
Also you need to assign the event before changing the image's src property.
Also note that there are numerous problems with the load event on images. From the jQuery manual on load():
Caveats of the load event when used with images
A common challenge developers attempt to solve using the .load() shortcut is to execute a function when an image (or collection of images) have completely loaded. There are several known caveats with this that should be noted. These are:
It doesn't work consistently nor reliably cross-browser
It doesn't fire correctly in WebKit if the image src is set to the same src as before
It doesn't correctly bubble up the DOM tree
Try this:
function loader(){
var imgH = this.height;
var imgW = this.width;
console.log(imgH, imgW);
}
var img = new Image();
img.onload = loader;
img.src ='../images/pic1.jpeg';
I used this way:
img.onload = function(){//here wrote everything from loader};
And it was the only working solution I have found.
I found the best way is to let the computer do the scaling first.
and declaring the onload = "some_function()" in the body.
<body onload="some_function()">
and then getting the sizing afterward in the script.
some_function(){
var image1 = document.getElementsByClassName('main')[0];
var computedStyle_image1 = window.getComputedStyle(image1);
var image_width = computedStyle_image1.getPropertyValue('width');
}
I noticed with google chrome you need to make sure you call the onload="function" within the body div otherwise the image values arn't set and it pulls in 0px for width and height.
This is driving me nuts - I have been round and round in circles, and this no joy. I am loading multiple dynamic images, and have a simple Javascript object which I instantiate for each image, and which has a callback to render the image once it has loaded asynchronously.
I have verified that the callback code works just fine on a stand-alone basis (ie. I can call the callback 'manually' after the image has loaded, and the image is rendered correctly), and I have verified that the image itself is successfully loaded (by switching the object's callback for a simple one line logging function), but when I try to tie it all together, the callback apparently never gets invoked.
I'm relatively new to JS and I suspect that I am missing something fundamental about the way functions within objects are defined, but despite a lot of Googling, can't work out what.
Please can someone show me the error of my ways?
function ImageHolder(aX,aY,aW,aH, anImageURL) {
this.posx=aX;
this.posy=aY;
this.posw=aW;
this.posh=aH;
this.url=anImageURL;
this.myImage = new Image();
this.myImage.onload=this.onload;
this.myImage.src=anImageURL;
this.onload=function() {
try {
var d=document.getElementById("d");
var mycanvas=d.getContext('2d');
mycanvas.drawImage(this.myImage, this.posx, this.posy, this.posw, this.posh);
} catch(err) {
console.log('Onload: could not draw image '+this.url);
console.log(err);
}
};
}
You have two problems: first, this.onload is not defined at the point at which you assign it to the image. You can fix this by missing out the stage of storing the onload handler function as a property of this. The second problem is that when the onload handler function is called, this is not set to what you think it is (in fact, it's a reference to the Image object that has just loaded). You need to store a reference to the current ImageHolder object and use it instead of this within the event handler function.
New code:
var that = this;
this.myImage = new Image();
this.myImage.onload=function() {
try {
var d=document.getElementById("d");
var mycanvas=d.getContext('2d');
mycanvas.drawImage(that.myImage, that.posx, that.posy, that.posw, that.posh);
} catch(err) {
console.log('Onload: could not draw image '+that.url);
console.log(err);
}
};
this.myImage.src = anImageURL;