Fact : The following code is valid.
var img = new Image();
img.onload = function() {
context.drawImage(img, 32, 32);
};
img.src = "example.png";
First Observation : The following will not draw to canvas.
var img = new Image();
img.src = "example.png";
context.drawImage(img, 32, 32);
Second Observation : The following will draw to canvas (eventually)...
var img = new Image();
img.src = "example.png";
setInterval(function() {context.drawImage(img, 32, 32);}, 1000);
Why is it that I need to call the drawImage function on a callback? And if that is the case, why does it eventually work when nested in a setInterval function?
When you set the src of the image object, the browser starts to download it. But you may or may not get that image loaded by the time the browser executes the next line of code. That's why you are drawing a "blank" image, because it ain't loaded just yet.
You need to place an onload handler to know when the image has finished loading. Only then will you draw it to the canvas:
var img = new Image(); //create image object
img.onload = function(){ //create our handler
context.drawImage(img, 32, 32); //when image finishes loading, draw it
}
img.src = "example.png"; //load 'em up!
You can only draw the image to canvas after it's loaded, that's why it works when you do it from the onload callback. And it works with setInterval because, after a certain amount of time, it eventually gets fully loaded.
I believe its because loading an image is not instant, it takes time. Once the image is loaded, then it can be drawn to the canvas
This is needed because the browser needs to "read" and eventually download the image (onload event) to correctly handle the image load. Using a setInterval to simulate this behaviour could not work, for example loading of a large image on a slow connection...
So the best way to do this is:
var img = new Image():
img.src = "image.jpeg";
img.onload = function() {
// Now you can play with your image.
}
Related
Got a image inside of a HTML document. Source is a base64 string. I want to retrieve the color of the pixel that is clicked on. Using a memory canvas all I get is zeros.
function getColor(imagecontainer,top,left){
var image = new Image();
image.onload = function() {
var canvas = document.createElement('canvas');
var context = canvas.getContext('2d');
context.drawImage(image, 0, 0);
var myData = context.getImageData(Math.round(left)-2, Math.round(top)-2, 4, 4).data;
console.log(myData, left, top);
};
image.src = imagecontainer.find("img").attr("src");
}
There are many other questions regarding the same problem, however none of the solutions could solve this problem for me. MyData always contains zeros.
Update based on new information:
The cause could simply be that the main image (imagecontainer.find("img")) isn't loaded at the time it is references.
If this image exists in DOM you can use windows.onload to run your script:
window.onload = function() {
// code that uses the image
};
as this will run only when everything has loaded incl. image data. Optionally add an inline onload handler to the image tag (not recommended), or add the src via JavaScript and monitor the onload event there.
Another possible cause is that if this is IE and the image is in the cache, onload may not trigger. You can check the image's complete property to check for this:
var image = new Image();
image.onload = onloadHandler;
image.src = imagecontainer.find("img").attr("src");
if (image.complete) onloadHandler();
function onloadHandler() {
var canvas = document.createElement('canvas');
var context = canvas.getContext('2d');
context.drawImage(image, 0, 0);
var myData = context.getImageData(Math.round(left)-2, Math.round(top)-2, 4, 4).data;
};
Although you can use this directly with the original image without loading another with the same url.
I made a jsFiddle which demonstrates pixel manipulation in JavaScript. It works perfectly fine in Chrome. Then I moved to test it on Firefox.
It doesn't work and it threw an error:
IndexSizeError: Index or size is negative or greater than the allowed amount
This confuses me. But wait, there's more.
When I click Run again, the code suddenly works. I don't know what sorcery is this, or it's just some weird Firefox bug.
You can see the problem here: http://jsfiddle.net/DerekL/RdK7H/
When you are in jsFiddle, click on Select File and select a PNG file. You should see the code is not working. Then you click Run. Do the same thing again, and it suddenly works.
There are also some problems in some of the functions in Firefox which also frustrated me, however it is not part of this question.
If you need to know, I'm using Firefox 26.0.
It is because your image hasn't completed loading yet so the default width and height of the image is returned (both 0). As you cannot use 0 for the width and height of getImageData() you get an error.
When I click Run again, the code suddenly works. I don't know what
sorcery is this, or it's just some weird Firefox bug.
It's because the image is now in the cache and the browser happens to be able to provide it before you attempt to read its width and height (no, the bug is in your code :-) ).
Handling image loading with a busy loop and a timeout value is begging to fail.
Make sure you add an onload handler to the image (this may require you to refactor the code a bit to support a callback (or promise) and the return value won't be valid for the same reason as the error):
getRGBArray: function(uri, callback){ /// add parameter for callback here
var image = new Image();
image.onload = imageLoaded; /// add an onload handler here
image.src = uri;
function imageLoaded() {
//var t = Date.now();
//while(Date.now() - t < 3000 && !image.width);
var width = this.width, /// replace image with this to be sure you
height = this.height, /// ..are dealing with the correct image in
canvas = $("<canvas>").attr({ /// ..case you load several ones..
width: width,
height: height
}).appendTo("body"),
ctx = canvas[0].getContext("2d");
ctx.drawImage(this, 0, 0);
var imgData = ctx.getImageData(0, 0, width, height).data;
...
callback(imgData); /// example of callback
}
...
}
Optionally separate the image loading so you can call this function without relying on if the image has loaded or not.
Update
As briefly mentioned you can separate the image loading from the your main code. For example - instead of loading your image in the getRGBArray() function, pre-load it somewhere else in the code and pass the image as an argument instead (callback cannot be avoided but you can keep your original code synchronous after the loading point):
function loadImage(url, callback) {
var image = new Image();
image.onload = function() {
callback(this);
}
image.src = uri;
}
Then call it for example like this:
loadImage(url, readyToGo);
function readyToGo(image) {
var pixels = getRGBArray(image);
...
}
A small modification in the original function to make it use the passed image instead of url:
getRGBArray: function(image){
var width = image.width,
height = image.height,
canvas = $("<canvas>").attr({
width: width,
height: height
}).appendTo("body"),
ctx = canvas[0].getContext("2d");
ctx.drawImage(image, 0, 0);
var imgData = ctx.getImageData(0, 0, width, height).data;
...
return opt;
}
...
}
Hope this helps!
does anyone know if it's possible to synchronously create an image object from a data uri using JavaScript? It is possible to create an image object from a data URI asynchronously like this:
imageObj = new Image();
imageObj.onload = function() {
callback(imageObj);
};
imageObj.src = dataURI;
You might think that this would work:
imageObj = new Image();
imageObj.src = dataURI;
callback(imageObj);
But if I remember correctly, this fails in some browsers.
Ideas?
I think you can use complete attribute of Image object.
http://www.w3schools.com/jsref/prop_img_complete.asp
Before calling callback function, in a loop, you can continuously check whether complete is true. Once its true, then it is loaded.
I want to get width of an external image with javascript.
I try this code :
var image = new Image();
image.src = "1.jpg";
alert(image.width);
but it get image width in firefox and get 0 in chrome.
why it not work in chromica?
Try
image.onload = function() {alert(this.width);}
You're trying to get the width before the image has been downloaded. You have to wait, e.g.:
var image = new Image();
image.onload = function() {
alert(image.width);
};
image.src = "1.jpg";
Note that it's important to hook onload before you set src, because otherwise you have a race condition. Even though JavaScript is single-threaded on browsers (unless you use web workers), the browser is not. It can fire the load event as soon as you set src and, seeing no handlers, not queue them for callback.
The idea of my code is create a hidden div which loads the image. When it's load event is fired draw it in the canvas. When I run the code I get this error 0x80040111 (NS_ERROR_NOT_AVAILABLE), yet I am waiting for the load event. Here is my code.
HTML
<div id="old-counties-image-wrapper" style="display: none;">
<img border="0" height="390" id="interreg-iiia-old-counties-map" src="/f/MISCELLANEOUS/old-map.jpg" /></div>
<p>
<canvas id="old-counties-image-canvas"></canvas></p>
and javascript
$('#interreg-iiia-old-counties-map').load(function() {
var canvas=document.getElementById('old-counties-image-canvas');
if (canvas.getContext) {
var ctx=canvas.getContext('2d');
var img=$('#interreg-iiia-old-counties-map');
ctx.drawImage(img, 0, 0);
}
//else {
// $('#old-counties-image-wrapper').show();
//}
});
The else part is commented out for now but is there for browsers that don't support canvas.
Because $('#interreg-iiia-old-counties-map') returns a jQuery object, while the drawImage method takes an Image object - the jQuery ($) function returns a jQuery object that wraps the original element to provide the usual jQuery functions you see.
You can get the underlying Image object by using the get method, but in this case it would be easier to just use this, which in the context of the callback function supplied to the load function, is the original $('#interreg-iiia-old-counties-map') DOM element. In other words,
ctx.drawImage(this, 0, 0);
should work fine here. You also don't have to use a hidden <img> element - with new Image you can retrieve the image similar to what you're doing here:
var img = new Image(),
canvas = document.getElementById('old-counties-image-canvas');
img.src = '/f/MISCELLANEOUS/old-map.jpg';
img.onload = function(){
if (canvas.getContext) {
var ctx = canvas.getContext('2d');
ctx.drawImage(img, 0, 0);
}
};
And, now for my own take on a solution:
var imgId = $("#myDiv img").attr("id");
var imgObj = document.getElementById(imgId);
var canvasContext = $("#imgCanvas")[0].getContext('2d');
canvasContext.drawImage(imgObj, 0, 0);
It's ugly, but I don't think it's much uglier than the solutions presented above. There may be performance advantages to other solutions as well, though this one seems to avoid needing to load the image file from the server's file system, which should be worth something. I tested it in Chrome, Firefox, and IE10.