Images not being drawn on HTML5 canvas everytime - javascript

I'm working on a web app with which users can construct a mesh containing simple objects (rectangular shaped) that can hold text and/or images. Users can create as many of these objects as they like, and in each object that can hold an image, upload one of their own. The app is online if you'd like to test it: goalcandy.com (use demo#demo.com / demo for the email and pswd fields to log in).
Once they finalize their work, they can convert that mesh to PNG. This is done by first drawing everything that's on the mesh on an HTML canvas, which is afterward converted to a PNG image, locally.
When needing to draw the array of existing images onto the canvas, I first have to preload them, which I do like this:
query = []; // array with paths to the image
var count = query.length,
images = [],
loaded = 0;
for (i=0; i<count; i++) {
images[i] = new Image();
images[i].onload = function(){
loaded++;
if (loaded == count)
drawOnCanvas();
};
images[i].src = query[i];
}
function drawOnCanvas() {
// draw the images onto the canvas
}
The problem with all of this is that on a slow, "tired" computer, the PNG sometimes turns out blank in the spaces where some user-uploaded images are supposed to be, because the images don't get drawn onto the canvas. As if they haven't finished downloading. On a good, fast computer, this doesn't happen. How can I solve this, and why does it happen? Considering that I've already attached a handler for the "load" event, I don't see the logic...

Related

html 5 canvas: using draw image in game makes loop very slow

i am making a game based on draw images and clear it every some part of second.
i started with:
var peng = new Image();
and then:
peng.onload = function() {
context.drawImage(peng, pengXPosition, pengYPosition, pengWidth, pengHight);
};
and in the loop:
var i=0;
function pengMoveRight(){ i++;if(i==1){peng.src = 'images/1.png';}else if(i==2)
{peng.src = 'images/2.png';} else if(i==3){peng.src = 'images/3.png';}else if(i==4){
peng.src = 'images/4.png';}else if(i==5){peng.src = 'images/5.png';}else if(i==6){
peng.src = 'images/6.png';i-=6;}}
when i run it it works well on IE but on chrome and mozilla it`s too slow and the character is about to disappear .. i used setinterval(); once and window.requestAnimationFrame(); once and both of them cause the same problem.
what should i do to make it smooth move?
here is the full script http://mark-tec.com/custom%20game/
Instead of changing the source, try to create several Image objects instead. That way, the drawImage call can always use a pre-loaded image.
You need to preload all the images or use the sprite method (all images packed into a single sprite) in order to avoid the initial delay caused by the image loading only when it's needed.
However, after that initial problem, your example should run fine once all the images are cached.

Save Canvas between pageloads

Ive been trying to make the canvas save between page refreshes using html5 local storage, but the canvas always gets back to blank when i refresh the page.
HTML
<canvas onload="loadCanvas()" onClick="canvas(event)" id="myCanvas" width="400" height="400">
Your browser does not support the canvas element
</canvas>
Javascript:
function saveCanvas() {
var c = document.getElementById("myCanvas"),
ctx = c.toDataURL();
if (typeof (localStorage) !== "undefined") {
localStorage.setItem('myCanvas', ctx);
} else {
document.getElementById("save").innerHTML.dataURL = "Local Storage not supported";
}
}
function loadCanvas() {
var image = localStorage.getItem('myCanvas');
document.getElementById('myCanvas').src = image;
}
saveCanvas function gets called when something has been drawed in the canvas
Anyone knows what the problem is?
It has been solved, onLoad did not work in canvas
localStorage can only save so much, ie. in most browsers 5 mb and in others less.
Another caveat is that each char stored takes 2 bytes due to unicoding so the storage is in reality only half of this in the practical sense. There is no guarantee about size as this is not defined by the standard - 5 mb is only a suggestion so browsers can use any size.
You are getting the image as a PNG as this is the default format of toDataURL(). If the produced data-uri is too large (which is likely here as base-64 adds 33% to the size + a small header) the save will truncate or fail depending on the browser.
This is most likely (as you don't state size of canvas or the resulting data-uri) why your canvas is blank when you try to reload the data-uri as it would be invalid.
You can try to save as JPEG instead:
dataUri = c.toDataURL('image/jpeg', 0.5); /// 0.5 is quality, higher is better
If this does not work then you will need to look into other local storage mechanisms such as Indexed DB (where you can request a storage quota) or File API (but this is only supported in Chrome at this moment). There is also the deprecated Web SQL which will be around for still a while.
Update
Also try to move your onload from canvas element to window:
window.onload = function() {
var image = localStorage.getItem('myCanvas');
document.getElementById('myCanvas').src = image;
}
Note: you cannot set a src on a canvas element (as the ID from your code here suggest as well as your example code show). You need an image element for that. When you set a src on an image you also need to use the onload handler on the image, so an example could be:
window.onload = function() {
var img = new Image;
img.onload = function() {
var ctx = document.getElementById('myCanvas').getContext('2d');
ctx.drawImage(img, 0, 0);
/// call next step here...
}
img.src = localStorage.getItem('myCanvas');
}
Usually I suggest (and others did too in this thread) people store their drawings as points and shape types in an array as objects which then is serialized to a string which you instead store in localStorage. It involves a little bit more code in the render stage (which you need anyways to update canvas when it is blanked for some reason) but is worth it.

Check if Images are loaded before gameloop

So far my program is working the way I want it to. This works fine:
// Player object
var player = {
x: 10,
y: 10,
draw: function () {
ctx.drawImage(playerImg, 0, 0);
...
Should I check if playerImg is loaded first, even though it works correctly so far?
Also, what is the best way to check. I was thinking about putting all the images in an array. Then check with the onLoad function. If they are all loaded then I will start the game loop. Is this a good idea?
Thanks
How image loading works
You need to check if the image is loaded as image loading is asynchronous. You may experience that your code works sometimes without. This is mainly because your image exists in the cache and the browser is able to load it fast enough before the drawImage is called, or the image exists on local disk.
However, new users will need to download the data first and you don't want first-time users to experience errors such as images not showing because they are not finished loading.
As it works asynchronous your code will continue to execute while the image loading takes place in the background. This may cause your code to execute before the image has finished loading. So handling image loading is important
Handling multiple images
You can load all your images first (or those you need to start with) and you can define them using array:
var imageURLs = [url1, url2, url3, ...],
images = [],
count = imageURLs.length;
Then iterate and create the image elements:
for(var i = 0; i < count; i++) {
/// create a new image element
var img = new Image();
/// element is valid so we can push that to stack
images.push(img);
/// set handler and url
img.onload = onloadHandler;
img.src = imageURLs[i];
/// if image is cached IE (surprise!) may not trigger onload
if (img.complete) onloadHandler().bind(img);
}
and in the callback function do the inventory count:
function onloadHandler() {
/// optionally: "this" contains current image just loaded
count--;
if (count === 0) callbackDone();
}
Your callback is the code you want to execute next. Your images will be in the array images in the same order as the imageURLs.
For production you should also incorporate an onerror handler in case something goes wrong.

Chrome / Firefox Cached Images Issue

I'm having trouble when preloading images and creating elements on the fly. The script loads a list of image URLs, creates canvas elements etc.
The problem is that on subsequent "soft" refreshes or submission of the same URL the preload loop isn't initiated, so the canvas elements etc. are not created. I'm pretty certain this has something to do with resource caching - however - why the script isn't iterating through the image URL list and building the DOM as expected I'm not sure... Here's an example
The JavaScript on the page is optimised output, but the preload loop looks like this:
// Images array
var images = [];
for (var i = 0; i < l.length; ++i) {
// Create canvas element
var canvas = document.createElement('canvas');
// Canvas element properties
canvas.width = l[i].w;
canvas.height = l[i].h;
canvas.style.display = 'none';
// Image element + mouse over event
images[i] = document.getElementById('i' + i).getElementsByTagName('img')[0];
images[i].addEventListener('mouseover', function() {
handleCanvas(this);
}, false);
// Push canvas into DOM
images[i].parentNode.insertBefore(canvas, images[i]);
// Preload item from processed images list
var image = new Image;
image.src = l[i].i;
}
I'd prefer not to resort to headers or "URL?random=37436464" type fixes if possible. Tested on nightly versions of Chrome and Firefox on Ubuntu 12.10.
After further investigation (testing in IE9) it appears the issue was cause by asynchronous loading of the JavaScript and lagging variable initiation.

Is there a way of "controlled" preloading of images (maybe with help of jQuery)?

For a sort of slideshow I'm trying to implement a clever image pre-loader, which should work as following:
On load of slideshow first image is shown and images start loading
in background in order 2, 3, 4, ...
If user decides to switch to image n (which can be done via
search or thumbs or ...) and the triggered preload (from 1.) didn't load n yet, the order (2, 3, 4, ...) established in 1. will be re-organized with n as first element.
Other possibility for 2. could be to suspend preloading, load image n, and continue preload, but I'd prefer first idea because this will give you the freedom of more sophisticated re-ordering, e.g. (n, n+1, n+2, ..., last, 1st-not-loaded, 2nd-not-loaded, ...).
So far I just did a simple preload-loop like that:
for (var i = 0; i < issue.pages.length; i++)
{
log("preloading image from URL " + issue.pages[i].imageUrl);
var img = new Image();
img.onload = function()
{
log("reading loading image " + this.src);
};
img.src = issue.pages[i].imageUrl;
}
which works fine, but seems to block downloading of other images: If I call
imageN = new Image();
imageN.src = issue.pages[n].imageUrl;
somewhere else (onClick of thumb) while images of loop still loading, imageN will not be loaded before it's its turn from order in loop.
Any ideas?
To my knowledge there is no way to stop or suspend an image loading with javascript. However, a neat way to control the order of the images loading would be to chain together their load events. Here is an example (using jQuery).
In the example I have an array imgQueue that I am using as a queue and a function loadNextImage that grabs an image off the queue to load and calls loadNextImage when the image is done loading.
function loadNextImage() {
if (imgQueue.length > 0) {
var imgN = new Image();
imgN.src = imgQueue.shift();
$(imgN).load(function(){
$("#main").append(imgN);
loadNextImage();
});
}
}
To change the order images are loading you would literally just move them ahead in imgQueue, for example if I wanted to move image 5 to the front:
imgQueue.unshift(imgQueue.splice(4,1));
This isn't perfect, for instance, unless the images are large it probably makes more sense to load them in groups. Also because loadNextImage manipulates the queue you can't keep track of images just by their original index, you'd need to do something fancy like store them as hashes so you can find them later:
var imgHashQueue = [
{name: "name", url = "url"},
...
]
Couple resources here:
http://ditio.net/2010/02/14/jquery-preload-images-tutorial-and-example/ - http://engineeredweb.com/blog/09/12/preloading-images-jquery-and-javascript
Previous SO discussion: Preloading images with jQuery

Categories