HTML 5 canvas DrawImage only working after refresh - javascript

I have an issue with drawing to the canvas, I have an array of image information that i loop through and create image objects from. On each images onload event i draw it to the canvas, however this will only work after a refresh (once the images are in the cache it seems).
Here is a snippet from my code
var image = new Image();
image.src = img.src; //got from elsewhere, need to draw a background image first
image.onload = function () {
var canvas = document.createElement('canvas');
canvas.width = img.width;
canvas.height = img.height;
img.parentNode.replaceChild(canvas, img);
var context = canvas.getContext('2d');
context.drawImage(image, 0, 0, canvas.width, canvas.height);
context.globalCompositeOperation = 'source-over';
//looping through my image info
for (var index in anaglyphs){
var anaglyph = anaglyphs[index];
if (anaglyph === undefined) continue;
//If x, y, height and width values are set in Image then set in mask
//otherwise use defaults
var x = 0;
if (anaglyph.x !== undefined) x = anaglyph.x;
var y = 0;
if (anaglyph.y !== undefined) y = anaglyph.y;
var width = canvas.width;
if (anaglyph.width !== undefined) width = anaglyph.width;
var height = canvas.height;
if (anaglyph.height !== undefined) height = anaglyph.height;
var alpha = new Image();
//Use of an intimidate function to stop javascripts closure
//overriding the variables before the onload event fired. Creating
//temporary variables in the intimidate function, which executes
//immediately, and storing the desired values inside them for callback.
(function (){
var xPos = x;
var yPos = y;
var maskWidth = width;
var maskHeight = height;
alpha.onload = function () {
context.drawImage(this, xPos, yPos, maskHeight, maskWidth);
};
})();
alpha.src = "data:"+anaglyph.content+";base64,"+encode64(anaglyph.data);
}
};
After a refresh it works fine and it will continue to work fine if I open the webpage using that script again, however it will fail again if I clear the cache and reopen the page. It only needs to work in the latest version of Firefox.
Thanks

Try putting alpha.src inside your closure:
(function (anaglyph,x,y,width,height){
var xPos = x;
var yPos = y;
var maskWidth = width;
var maskHeight = height;
alpha.onload = function () {
context.drawImage(this, xPos, yPos, maskHeight, maskWidth);
};
alpha.src = "data:"+anaglyph.content+";base64,"+encode64(anaglyph.data);
})(anaglyph,x,y,width,height);
BTW, why use a closure?
How about just a simple image loader with callback instead (just an example):
// callback -- after all images are loaded
function draw(images){
// or whatever you want in your callback
context.drawImage(images.image1, 10, 10, 50,50 );
context.drawImage(images.image2, 10, 100, 50,50);
}
var images = {};
var URLs = {
image1: 'ship.jpg',
image2: 'houseicon.png'
};
LoadImages(URLs, draw );
function LoadImages(URLs, callback) {
var loaded = 0;
var needed = 0;
for(var url in URLs) { needed++; }
for(var url in URLs) {
images[url] = new Image();
images[url].onload = function() {
if(++loaded >= numImages) {
callback(images);
}
};
images[url].src = URLs[url];
}
}

Related

Images not rendering randomly on canvas

Image of the bug is appearing only at one place i.e. x = 0,y = 0.
var canvas = document.querySelector('canvas');
canvas.width = window.innerWidth;
canvas.height = window.innerHeight;
var c = canvas.getContext('2d');
var bugSmashed = 0;
//rendering background image
function renderImage()
{
var backImage = new Image();
backImage.src = "jungle.jpg"
backImage.onload = function(){
c.drawImage(backImage,0,0,canvas.width,canvas.height)}
}
renderImage();
//Making a bug
class Bug {
constructor(x,y){
this.x = x;
this.y = y;
}
renderBug(){
var bugImage = new Image();
bugImage.src = "bug.png";
bugImage.onload = function(){
c.drawImage(bugImage,this.x,this.y,65,65)}}
}
Trying to make the bug appear randomly on the canvas
var interval = setInterval(function(){
var x = 32+Math.random()*(canvas.width-64);
var y = 32+Math.random()*(canvas.height-64);
var aBug = new Bug(x,y);
aBug.renderBug();}, 2000);
I am sure I am missing something. Any help is appreciated. Thanks
I found your problem: this changes its meaning inside a function. So when you use:
function(){c.drawImage(bugImage,this.x,this.y,65,65)}
this no longer refers to aBug! (It will instead refer to the global Window object.) You can use fat-arrow syntax instead (which preserves this):
() => c.drawImage(bugImage,this.x,this.y,65,65)
Another really ugly way which I discourage you from using is by creating a new reference to this, and then using that reference in your function:
let that = this;
function(){ c.drawImage(bugImage, that.x, that.y, 65, 65); };
Or you can simplify your code to have it gloss over onload logic, allowing you to avoid wrapping c.drawImage in a function in the first place (note the square which appears is a publicly addressable image):
var canvas = document.querySelector('canvas');
canvas.width = window.innerWidth;
canvas.height = window.innerHeight;
var c = canvas.getContext('2d');
//Making a bug
let bugImg = new Image();
bugImg.src = "https://th.bing.com/th/id/OIP.pXD0MAw4LeAcVrt3qRiEfwAAAA?pid=ImgDet&rs=1";
class Bug {
constructor(x,y){
this.x = x;
this.y = y;
}
renderBug(){
c.drawImage(bugImg, this.x, this.y, 65, 65);
}
}
var interval = setInterval(function(){
var x = 32 + Math.random()* (canvas.width - 64);
var y = 32 + Math.random()* (canvas.height - 64);
var aBug = new Bug(x, y);
aBug.renderBug();
}, 500);
<canvas id="canvas" width="100" height="100"></canvas>

How do I create and draw an array of images on a canvas using Javascript?

I am trying the following code to allow me to create an image, allow custom values attached to it and draw in on an HTML canvas. It doesn't seem to be working though.
var canvas = document.getElementById('mainCanvas');
var context = canvas.getContext('2d');
function CreateObject(objectImage){
lastIndex++;
PlanObjects[lastIndex] = new PlanObject(lastIndex,currentSlide,24,24,24,24,objectImage);
context.drawImage(PlanObjects[lastIndex].img,PlanObjects[lastIndex].posX,PlanObjects[lastIndex].posY);
}
var PlanObjects = [];
var lastIndex = -1;
var currentSlide = 0;
function PlanObject(id, slide, width, height, posX, posY, img){
this.id = id;
this.slide = slide;
this.width = width;
this.height = height;
this.posX = posX;
this.posY = posY;
this.img = new Image(img);
}
CreateObject("imgs/icons_star.png");
HTML:
<div id="mainCanvasDiv"><canvas width="800" height="600" id="mainCanvas"></canvas>
I expect to create the object, store it in the array of PlanObjects (with it's customer values) and draw it onto the canvas but it doesn't seem to do that.
1) The input parameters for the image constructor are width and height. And you must separately set the source:
var myImage = new Image(100, 200);
myImage.src = 'picture.jpg';
document.body.appendChild(myImage);
[ https://developer.mozilla.org/en-US/docs/Web/API/HTMLImageElement/Image ]
2) Loading an image is asynchronous, so you need to handle the load event:
PlanObjects[lastIndex].img.addEventListener('load', function() {
context.drawImage(PlanObjects[lastIndex].img, PlanObjects[lastIndex].posX, PlanObjects[lastIndex].posY)
});
[ https://developer.mozilla.org/en-US/docs/Web/Events/load ]
[ https://jsfiddle.net/bvupyroc/ ]
I had to modify the new Image line to fix the problem
var canvas = document.getElementById('mainCanvas');
canvas.width = canvas.scrollWidth;
canvas.height = canvas.scrollHeight;
var context = canvas.getContext('2d');
function CreateObject(objectImage){
lastIndex++;
PlanObjects[lastIndex] = new PlanObject(lastIndex,currentSlide,24,24,24,24,objectImage);
context.drawImage(PlanObjects[lastIndex].img,PlanObjects[lastIndex].posX,PlanObjects[lastIndex].posY);
}
var PlanObjects = [];
var lastIndex = -1;
var currentSlide = 0;
function PlanObject(id, slide, width, height, posX, posY, img){
this.id = id;
this.slide = slide;
this.width = width;
this.height = height;
this.posX = posX;
this.posY = posY;
this.img = new Image();
this.img.src = img;
}
CreateObject("./imgs/icons_star.png");

Drawing image on canvas in loop not working

var canvas = document.getElementById("canvas");
wheel = canvas.getContext("2d");
for (var i = 0; i < 10; i++) {
var img = new Image;
img.src = image;
wheel.drawImage(img, -170, -10, 140, 140);
}
My above code draw only 9(Nine) images but lastOne(10th) image not drawing on canvas. I tried above code but not getting success. Anyone can know solution for this problem.
Can any one help me to find out online JSFiddle Demo which draw image on canvas in loop.
Perhaps your image isn't finished loading before your first draw attempt.
Here is the syntax to make sure your image has finished loading:
var canvas = document.body.appendChild(document.createElement("canvas"));
canvas.width = 1100;
canvas.height = 110;
var wheel = canvas.getContext("2d");
var img = document.createElement("img");
img.onload = function() {
for (var i = 0; i < 10; i++) {
wheel.drawImage(img, 5 + 110 * i, 5);
}
};
img.src = "https://placeholdit.imgix.net/~text?txtsize=60&txt=1&w=100&h=100";
EDIT 1 - PROMISES
Working with multiple images:
List sources
map to generates DOM nodes
Setup a Promise per DOM node in a Promise.all
Then draw images
var canvas = document.body.appendChild(document.createElement("canvas"));
canvas.width = 1100;
canvas.height = 110;
var wheel = canvas.getContext("2d");
var images = [
"https://placeholdit.imgix.net/~text?txtsize=60&txt=1&w=100&h=100",
"https://placeholdit.imgix.net/~text?txtsize=60&txt=2&w=100&h=100",
"https://placeholdit.imgix.net/~text?txtsize=60&txt=3&w=100&h=100",
"https://placeholdit.imgix.net/~text?txtsize=60&txt=4&w=100&h=100",
"https://placeholdit.imgix.net/~text?txtsize=60&txt=5&w=100&h=100",
"https://placeholdit.imgix.net/~text?txtsize=60&txt=6&w=100&h=100",
"https://placeholdit.imgix.net/~text?txtsize=60&txt=7&w=100&h=100",
"https://placeholdit.imgix.net/~text?txtsize=60&txt=8&w=100&h=100",
"https://placeholdit.imgix.net/~text?txtsize=60&txt=9&w=100&h=100",
"https://placeholdit.imgix.net/~text?txtsize=60&txt=10&w=100&h=100",
]
.map(function(i) {
var img = document.createElement("img");
img.src = i;
return img;
});
Promise.all(images.map(function(image) {
return new Promise(function(resolve, reject) {
image.onload = resolve;
});
}))
.then(function() {
for (var i = 0; i < images.length; i++) {
var img = images[i];
wheel.drawImage(img, 5 + 110 * i, 5);
}
});
I would do the similar approach to Emil, only difference having a method that loads them individually and once the image is loaded, try to load the next one and so on ...
take a look:
var canvas = document.getElementById("canvas");
// setting canvas size
canvas.height = window.innerHeight - 10;
canvas.width = window.innerWidth - 10;
var image_test = document.querySelector(".hidden");
var count = 0;
var total_images = 10;
var wheel = canvas.getContext("2d");
function loadImage() {
var img = new Image();
img.onload = function() {
wheel.drawImage( this , 105 * count , 0 , 100, 100);
if( count < total_images ) {
count++;
loadImage();
}
}
// img.src = "image-" + count + ".jpg";
img.src = "https://placeholdit.imgix.net/~text?txtsize=60&txt=1&w=100&h=100";
}
loadImage();
https://jsfiddle.net/gugalondon/r5xw6u7L/7

Image.width and Image.height equal to 0 after I load images (javascript)

I tried multiple times to assign my image.width and image.height to another variable but the value of the variable is always 0.
This is my code:
//canvas element
var canvas = document.createElement ('canvas');
var canvasWidth = window.innerWidth ;
var canvasHeight = window.innerHeight;
canvas.width = canvasWidth;
canvas.height = canvasHeight;
//img element
var loadImage = function (src, scale){
var image = document.createElement ('img');
image.onload = function(){
image.width*=scale;
image.height*=scale;
}
image.src=src;
return image;
}
//player function
var Player = function (){
this.image = loadImage("Fish1.png", 1);
this.score = 0;
this.width = this.image.width;
this.height = this.image.height;
this.x = window.innerWidth/2;
this.y= window.innerHeight/2;
}
Player.prototype.update = function (){
}
Player.prototype.render = function (){
//context.drawImage(this.image, this.x, this.y, this.width, this.height);
}
//context
var context = canvas.getContext('2d');
//player object
var player = new Player ();
window.onload = function (){
document.body.appendChild(canvas);
//context.drawImage(player.image, player.x, player.y, 100, 100);
}
as u can see, after I did some google searches, I found that I had to first load my images using image.onload, and I did that but I still cannot assign the value of image.width. For example if I put document.write(player.width) or even document.write(player.image.width) the value 0 is printed. But then I tried printing out player.image.width in window.onload function and it prints out the correct value. Can you please tell me what I am missing and how I can solve this problem.
You are still accessing the image's dimensions before it loaded. Simply adding onload does not solve your problem. You still have to access the dimensions after the image loaded
One solution would be to load the images first and then create player objects with the image.
You can have a static method which provides a nice API:
function Player(image) {
this.image = image;
// ...
}
Player.withImage = function(src, scale, done) {
var image = document.createElement('img');
image.onload = function(){
image.width *= scale;
image.height *= scale;
// once the image loaded, we create a new player and pass it to the callback
done(new Player(image));
};
image.src = src;
};
Player.withImage("Fish1.png", 1, function(player) {
// context.drawImage(player.image, player.x, player.y, 100, 100);
});
Of course this is a very simple example for a single player. You can keep the image loading code separate from the player class. If you have multiple images to load, you might want to look into promises.

A canvas into which I would like to load a Base64 string that represents a png image but just displays a blank white box

I have a canvas into which I would like to load a Base64 string that represents a png image. However, the following code just displays a blank white box and I am baffled as to why. When I look at the data in the canvas, it looks identical to a canvas that gets its data from a FileReader object (also below). Any help deducing this issue is greatly appreciated!
This code shows a white canvas:
html
<canvas id="canvas" width="114" height="114" style="z-index: 999999; display: none; padding-left: 50px"></canvas>
javascript
var websiteIconData = $('#ctl00_ContentPlaceHolder1_websiteIcon');
if (websiteIconData.val() != '') {
var canvas = document.getElementById('canvas');
var ctx = document.getElementById('canvas').getContext('2d');
var loadedImg = new Image();
loadedImg.onload = function () {
ctx.drawImage(this, 0, 0);
Debugger.log(ctx);
};
loadedImg.src = websiteIconData.val();
canvas.style.display = 'block';
}
This code shows the image:
$('#loadWebsiteIcon').on({
change: function (ev) {
var reader = new FileReader();
reader.onload = function (e) {
function draw() {
var ctx = document.getElementById('canvas').getContext('2d');
var img = new Image();
img.onload = function () {
var MAX_WIDTH = 114;
var MAX_HEIGHT = 114;
var width = img.width;
var height = img.height;
if (width > MAX_WIDTH) {
width = MAX_WIDTH;
}
if (height > MAX_HEIGHT) {
height = MAX_HEIGHT;
}
ctx.drawImage(img, 0, 0, width, height);
for (var i = 0; i <= document.images.length; i++) {
}
Debugger.log(ctx);
};
img.src = e.target.result;
}
draw();
};
reader.readAsDataURL(ev.target.files[0]);
var canvas = document.getElementById('canvas');
canvas.style.display = 'block';
var imgData = canvas.toDataURL();
$('#ctl00_ContentPlaceHolder1_websiteIcon').val(imgData);
Debugger.log(imgData);
}
});
Be careful how Base64 is parsed. If it is parsed for email, it will insert a character ever 76 lines by default. Most Base64 encoders have an option to turn this off. I am looking at MIME::Base64
From that document :
The returned encoded string is broken into lines of no more than 76 characters each and it >will end with $eol unless it is empty.
where $eol was one of the arguments. In the case of this module, setting it to an empty string would prevent the base64 from being broken up.
It turns out the issue had little to do with the canvas API or the Base64 encoding. It was more an issue of the canvas.toDataURL(); function getting called before the image had been loaded in the canvas. Moving this line to within the img.onload function seemed to do the trick. Below is the correct code:
function initImageChange() {
//this is will be a string in Base64, in this case it is an <input type="text">... tag
var imageData = $('#imageData');
//this is a canvas tag in on the page
var canvas = document.getElementById('canvas');
//load the image from the imageData var if it exists
if (imageData.val() != '') {
var ctx2=canvas.getContext('2d');
var loadedImg = new Image();
loadedImg.onload = function() {
ctx2.drawImage(this, 0, 0);
};
loadedImg.src = imageData.val();
canvas.style.display = 'block';
}
//this is a function that loads an image from a file onto the canvas and
//sends the Base64 representation of this image to the text input tag. This
//could fairly easily be send directly to a post request or saved some other way
$('#loadImage').on({
change: function (ev) {
var ctx = document.getElementById('canvas').getContext('2d');
var dataURL = '';
var reader = new FileReader();
reader.onload = function (e) {
ctx.clearRect(0, 0, canvas.width, canvas.height);
function draw() {
var img = new Image();
img.onload = function () {
var MAX_WIDTH = 130;
var MAX_HEIGHT = 110;
var width = img.width;
var height = img.height;
if (width > MAX_WIDTH) {
width = MAX_WIDTH;
}
if (height > MAX_HEIGHT) {
height = MAX_HEIGHT;
}
ctx.drawImage(img, 0, 0, width, height);
dataURL = canvas.toDataURL();
$('#ctl00_ContentPlaceHolder1_websiteIcon').val(dataURL);
ctx.restore();
};
img.src = e.target.result;
}
draw();
};
reader.readAsDataURL(ev.target.files[0]);
canvas.style.display = 'block';
}
});
}

Categories

Resources