HTML5 Image.onload race condition in Internet Explorer 9 - javascript

I've found similar timing issues concerning IE 9 and drawing images to HTML5 cnavases discussed on stackoverflow, but none quite match mine.
I'm encountering an issue such that periodically the image's onload function is called, but it is still not ready to be drawn to a canvas. (The canvas remains blank). Within onload, the image's "complete" property is always true, and the "width" and "height" properties always contain the expected values. No exceptions are thrown. If I put a small delay in onload prior to drawing the image to the canvas, I never get a blank draw. I can also tie another attempt to draw the image to an event like a mouse click, and this is always successful. I do not observe this in Chrome or Firefox. I appreciate any insights. Thanks!
var canvas = document.createElement('canvas');
canvas.width = 640;
canvas.height = 480;
var context = canvas.getContext("2d");
var image = new Image();
image.onload = function()
{
// this.complete always true here
// A delay here seems to guarantee the draw will succeed
context.drawImage(this, 0, 0); // Half the time canvas remains blank after draw
// If the canvas is still blank, a later call to context.drawImage with this image will succeed
}
image.src = "some.png";

I don't see where you append the canvas to the body--is that intentional?
Anyway, try putting the createElement inside the onload
Does this make things consistently good?
var canvas;
var context;
var image = new Image();
image.onload = function()
{
canvas = document.createElement('canvas');
canvas.width = 640;
canvas.height = 480;
document.body.appendChild(canvas);
context = canvas.getContext("2d");
context.drawImage(this, 0, 0); // Half the time canvas remains blank after draw
}
image.src="https://dl.dropboxusercontent.com/u/139992952/stackoverflow/city-q-c-640-480-6.jpg";

Related

Run requestAnimationFrame in background

I am using requestAnimationFrame to animate video streams and I know that request animation does not work in backgrounds so is there any way to make it run on background.
I know that we can animate using setInterval or setTimeout too but when I use these, animations were not work properly and images on video stream were blinking.
Here is my code:
const drawFrame = function drawFrame() {
if (!ctx) {
ctx = canvas.getContext('2d');
}
if (!tmpCanvas) {
tmpCanvas = document.createElement('canvas');
tmpCtx = tmpCanvas.getContext('2d');
tmpCanvas.width = canvas.width;
tmpCanvas.height = canvas.height;
}
tmpCtx.drawImage(videoElement, 0, 0, tmpCanvas.width, tmpCanvas.height);
const imgData = tmpCtx.getImageData(0, 0, tmpCanvas.width, tmpCanvas.height);
const data = selectedFilter(imgData);
ctx.putImageData(data, 0, 0);
if (!stopped) {
requestAnimationFrame(drawFrame);
} else {
tmpCanvas = null;
tmpCtx = null;
ctx = null;
}
};
requestAnimationFrame(drawFrame);
I guess you are out of luck. requestAnimationFrame intentionally pauses in the background because animations have no reason to be running in the background, and as you mention setInterval and setTimeout are not designed for animation and should not be used as such.
You mention that you need to animate a video stream. Do you mean that you are applying an animation on top of a video tag, or changing the appearance of the video element via a canvas element? In both cases, you shouldn't continue to do so in the background anyway, but you may give computeFrame (Firefox only) a go, which will fire once every frame of the video. Or is it that you are actually faking a video stream by animating a set of static images in succession? In that case, why are you not using the video element?

html5 canvas memory leak

I am developing a camera based SPA. When I'm trying to resize the image it cause to a memory leak.
without executing resize
first executing resize
.
second executing resize
The memory never go down until I do refresh.
It is strange the same app http://shitu.baidu.com does release the memory.
Here is the my resize function.
var useRightPen = function (img, pre_img_wid, pre_img_hei, aft_img_wid, aft_img_hei, callback) {
var canvas = document.createElement('canvas');
var ctx=canvas.getContext('2d');
canvas.setAttribute("height", aft_img_hei);
canvas.setAttribute("width", aft_img_wid);
var orientation = 1;
if (!!(/http/.test(img.src))) {
//memory keeps the same before the drawimage
canvas.getContext('2d').drawImage(img, 0, 0, aft_img_wid, aft_img_hei);
//memory goes up after the draimage
callback(canvas.toDataURL('image/jpeg'), aft_img_wid, aft_img_hei);
}
canvas = null;
}
Appreciate for everyone.
---update--06-11--2015
I make one fildde http://jsfiddle.net/onTheRoad/dc17guba/
The demo will reset the image src after every pick.Memory goes high on its way.

How can you overlay an image on a canvas, then draw on top of it?

I have all the code worked out to draw on a canvas in a separate .js file, so now I just need to put the image on.
I can’t get the image to work, though, the normal method isn’t working. I’ve tried this:
var canvasvar;
var contextvar;
canvasvar = document.getElementById("canvas");
contextvar = canvas.getContext("2d");
var imageObj = new Image();
imageObj.src = "http://wannabevc.files.wordpress.com/2010/09/im-cool.jpg";
contextvar.drawImage(imageObj,10,10);
For one thing, should I put this inside the html file, inside a <script></script> at the end, or should I put it in the js file that has the “draw stuff” code? I can’t get either to work, it runs with no errors but I don’t see any image over the canvas. So I can still draw, but no image appears. I’m guessing the image is under the canvas or something.
I’m guessing this is because I didn’t just make a canvas – I made it with all this programming happening inside it in another js file.
I also tried putting this code inside the initCanvas function in my .js code. This is what actually creates the canvas. Here is the code of initCanvas function:
// Retrieve canvas reference
canvas = document.getElementById("canvas");
// Size canvas
canvas.width = 600;
canvas.height = 400;
// Retrieve context reference, used to execute canvas drawing commands
context = canvas.getContext('2d');
context.lineCap = "round";
// Set control panel defaults
document.getElementById("thickness").selectedIndex = 0;
document.getElementById("color").selectedIndex = 1;
// Overlay image over the canvas. THIS PART IS WHAT I'M ASKING ABOUT. This is usually
// not in the code
var canvasvar;
var contextvar;
canvasvar = document.getElementById("canvas");
contextvar = canvas.getContext("2d");
var imageObj = new Image();
imageObj.src = "http://wannabevc.files.wordpress.com/2010/09/im-cool.jpg";
contextvar.drawImage(imageObj,10,10);
So the last part in this code is what I’m talking about. It isn’t working, though.
Give your image time to load using imageObj.onload
var canvasvar = document.getElementById("canvas");
var contextvar = canvas.getContext("2d");
var imageObj = new Image();
imageObj.onload=function(){
contextvar.drawImage(imageObj,10,10);
}
imageObj.src = "http://wannabevc.files.wordpress.com/2010/09/im-cool.jpg";

Saving canvas image in phonegap after canvas is drawn

I am creating a image in a canvas and saving the image. I found a very nice plugin here.
Code for the image saving:
var canvas = document.getElementById('myCanvas');
var context = canvas.getContext('2d');
var imageObj = new Image();
imageObj.src = imageURI;
imageObj.onload = function() {
contentW = $("#content").width();
canvas.width = 400;
canvas.height = 600;
context.drawImage(imageObj, 0, 0,canvas.width,canvas.height);
//the plugin
setTimeout(function(){
window.savephotoplugin(canvas,"image/png",device.version,function(val){
//returns you the saved path in val
alert("Photo Saved: " + val);
});
},3000)
}
The plugin works very nice only problem is that it is done before the canvas is even drawn. So I put a setTimeout to avoid it, however is there a way to detect when canvas is done and call the function after it. Tried jquery .change() didn't work.
If anyone finds this code useful feel free to use and the plugin is very nice :)
As you can see drawImage doesn't accept any callbacks. Also canvas doesn't define any events about its drawing process. So you choice of timeout is correct. Only one thing you could possibly improve. Use setTimeout(..., 0) instead of setTimeout(..., 3000). More details about this trick here

loop to create multiple canvas with multiple images not always displaying images

this may be an odd one to explain:
I want to create dynamically a collection of canvas that will have a single image in it. And this displays (after much hassle and getting rid of the onload event) but when I try to refresh the page I sometimees get nothing on the screen.
And when I was using thee onload event (to wait until the image is loaded) it would not display or display everything in the last canvas.
Here is a snippet of the code:
var sources = []//the array that contains the images
var divCanvas = document.getElementById('showcase-wrapper')
for(var i=0; i<sources.length; i++){
img = new Image()
img.src = sources[i]
canvas = document.createElement('canvas')
$(canvas).attr('id', i)
canvas.height=300
canvas.width=200
var context = canvas.getContext('2d');
//onload commented out allows expected display
//img.onload = function() {
context.drawImage(img, 0, 0);
//}
divCanvas.appendChild(canvas)
}
I have seen many posts that seemed to look like mine and tried quite a few but to no avail.
I think the issue is that var context is overwritten before the img.onload event is triggered. The img.onload event only has a reference to global scope. This is why only the last image shows up. You need to find the correct context inside the event.
I can't test your code directly, but I think it should be something like this:
var sources = []//the array that contains the images
var divCanvas = document.getElementById('showcase-wrapper')
for(var i=0; i<sources.length; i++){
img = new Image();
img.src = sources[i];
img.id = i; //Allow img to remember its corresponding canvas/context
canvas = document.createElement('canvas');
$(canvas).attr('id', i);
canvas.height=300;
canvas.width=200;
var context = canvas.getContext('2d');
divCanvas.appendChild(canvas);
img.onload = function() {
//Use the image id to get the correct context
var canvas = document.getElementById(this.id);
var context = canvas.getContext('2d');
context.drawImage(this, 0, 0);
}
}
For consistent behavior, you must use onload. If you don't the canvas drawing code may execute before the image is loaded and the desired image will not be drawn.
It might be that the onloads that would draw to the other canvases are not being called because the Images are getting garbage collected before the event can fire.
Try adding
var images = [];
At the start of your code and
images.push(img);
After the img = new Image() line.
If that doesn't work, try adding those images to the DOM tree-- img.setAttribute('style', 'display: none') first so you don't see them and they don't interfere with the document structure.
I found the fix here:
https://developer.mozilla.org/docs/Web/Guide/HTML/Canvas_tutorial/Using_images#Drawing_images
Apparently they have dropped img.onload to have the function called when the body has finished loaded.
Though, I still do not understand why my script or, ellisbben solution was not working.
If anyone as an answer for it, it would be very welcomed...

Categories

Resources