I know this question has been asked many times. However, I have used img.onload to properly execute what will be drawn to the canvas after the image is loaded. I am new to javascript and having trouble figuring it out. I have done the whole code in a function and calling it from the init() function. This init() function triggers when the body is loaded.
I have two images here. Both of them does not load at the first time.
function getAboutMeImage(){
var galleryCanvas = document.createElement("Canvas");
var galleryContext = galleryCanvas.getContext('2d');
galleryCanvas.width = 800;
galleryCanvas.height = 600;
galleryCanvas.style.position = "absolute";
galleryCanvas.style.display = "block";
galleryCanvas.style.top = "50%";
galleryCanvas.style.left = "50%";
galleryCanvas.style.marginLeft = "-400px";
galleryCanvas.style.marginTop = "-250px";
galleryCanvas.setAttribute("id","canvas2");
//galleryCanvas.style.background = "#FFF";
document.body.appendChild(galleryCanvas);
var points = [];
points[0] = new Array(0.3,1.3,0.55,1.05,2.9,1.05,3.1,0.75,9.25,0.75,9.9,1.3,9.9,6.3,9.6,6.6,5.75,6.6,5.3,7.0,2.5,7.0,1,4.3,0.3,4.3,0.3,1.3);
var points1 = [];
points1[0] = new Array(3.2,1,9.25,1,9.25,6.4,3.2,6.4,3.2,1);
drawPoly(galleryCanvas.width,0,0,11.0,7.0,galleryCanvas,points,"rgba(194,242,227,.2)",'#c2f2e3',10);
drawPoly(galleryCanvas.width,0,0,11.0,7.0,galleryCanvas,points1,"#000","#000",0);
//debugCanvas(galleryCanvas);
var y = Math.round((7.0 / 11.0) * galleryCanvas.width);
var width = Math.round(galleryCanvas.width / 11.0);
var height = Math.round(y / 7.0);
var img = new Image();
img.onload = function() {
var canvas2 = document.getElementById("canvas2");
var ctx2 = galleryCanvas.getContext("2d");
ctx2.drawImage(img,0.4 * width, 1.2*height, img.width, img.height);
};
img.src = "../logo.png";
var ratio = img.height * 1.0/ img.width;
img.width = Math.round(width*2.7);
img.height = Math.round(ratio * img.width);
var img2 = new Image();
img2.src = "../about me background2.png";
var ratio = img2.height * 1.0/ img2.width;
img2.width = Math.round(width*2.8);
img2.height = Math.round(ratio * img.width);
img2.onload = function(){
var ctx3 = galleryCanvas.getContext("2d");
ctx3.drawImage(img2,0.4 * width, 2*height, img2.width, img2.height);
}
setupAboutMe(galleryCanvas,galleryCanvas.width,20,0);
return galleryCanvas.toDataURL();
}
So, what am I doing wrong?
General advice:
Are both the onload callbacks being triggered?
If not, check the image .src.
Also, check the values you are sending into drawImage.
Are the resulting x,y off the visible canvas area?
Are the resulting width,height zero or near zero?
Related
I am trying to crop an image with Javascript. I have croppr.js and it is sending the data and I was trying to crop the image so I can save it to a storage server with Base 64. I have read online about .drawimage(). I have tried:
function process(data) {
console.log("DATA:" + data)
var canvas = document.getElementById("canvas")
var context = canvas.getContext('2d');
var imageObj = new Image();
var zoom
imageObj.onload = function() {
context.width = data.width
context.height = data.height
// draw cropped image
var sourceX = data.x;
var sourceY = data.y;
var sourceWidth = data.width / 2;
var sourceHeight = data.height / 2;
var destWidth = sourceWidth;
var destHeight = sourceHeight;
var destX = 0;
var destY = 0;
context.drawImage(imageObj, sourceX, sourceY, sourceWidth, sourceHeight, destX, destY, destWidth, destHeight);
var dataurl = canvas.toDataURL('image/jpeg', 1.0)
console.log(dataurl)
};
imageObj.src = document.getElementsByTagName("img")[0].src;
console.log(imageObj.src)
}
data Contains X Y Height Width As an JSON array.
First I see is the:
context.width = data.width
context.height = data.height
Did you meant to do canvas instead?
Here is an example:
function process(data) {
var canvas = document.getElementById("canvas")
var context = canvas.getContext('2d');
var img = new Image();
img.onload = function() {
canvas.width = data.width
canvas.height = data.height
// draw cropped image
var w = data.width / 2;
var h = data.height / 2;
context.drawImage(img, data.x, data.y, w, h, 0, 0, w, h);
};
img.src = data.src;
}
process({x:0, y:0, width:600, height: 600, src: "http://i.stack.imgur.com/UFBxY.png"})
<canvas id="canvas"></canvas>
That is the only issue I could see on your code.
What is not clear is the data you use to call the process function
I am currently trying to resize base64 images, since the image files are too big to be processed later on with php. I've found a way to achieve this by resizing the image using canvas. Unfortunately the image I get is just a black field which is 300px wide and 150px high. Maybe it has something to do with img.onload and canvas.toDataURL() order, or I am just using the wrong event (img.onload). Any idea where the mistake can be?
function exportImg(val){
var imageData = $('#image-cropper').cropit('export', {originalSize: true});
imageData = imageData.replace(/^data:image\/[a-z]+;base64,/, "");
var imageDataRes = resize(imageData);
$.post('php/upload.php', { imageDataRes: imageDataRes });
}
function resize(base64){
// Max size for thumbnail
var maxWidth = 900;
var maxHeight = 900;
// Create and initialize two canvas
var canvas = document.createElement("canvas");
var ctx = canvas.getContext("2d");
var canvasCopy = document.createElement("canvas");
var copyContext = canvasCopy.getContext("2d");
// Create original image
var img = new Image();
img.src = base64;
img.onload = function(){
// Determine new ratio based on max size
var ratio = 1;
if(img.width > maxWidth) {
ratio = maxWidth / img.width;
}
else if(img.height > maxHeight) {
ratio = maxHeight / img.height;
}
// Draw original image in second canvas
canvasCopy.width = img.width;
canvasCopy.height = img.height;
copyContext.drawImage(img, 0, 0);
// Copy and resize second canvas to first canvas
canvas.width = img.width * ratio;
canvas.height = img.height * ratio;
ctx.drawImage(canvasCopy, 0, 0, canvasCopy.width, canvasCopy.height, 0, 0, canvas.width, canvas.height);
}
alert(canvas.toDataURL());
return canvas.toDataURL();
}
EDIT:
What is async in this case and how to solve it? Sorry, but unfortunately I don't see how this could help me further. The $.post works perfectly, I get the images. I just don't get the idea of img.onload and toDataURL() and how I should parse them from one function to another. At first, I got a blank result, with no string at all (just data,), but by adding this img.onload I got finally some base64 string...but it was just black screen.
You will need to wait onload event after that use global variable to save data and call upload function on the end .
Try this :
function exportImg(val){
var imageData = $('#image-cropper').cropit('export', {originalSize: true});
imageData = imageData.replace(/^data:image\/[a-z]+;base64,/, "");
resize(imageData);
}
var SendWhenisReady = function(imageDataRes){
$.post('php/upload.php', { imageDataRes: imageDataRes });
};
function resize(base64){
// Max size for thumbnail
var maxWidth = 900;
var maxHeight = 900;
// Create and initialize two canvas
var canvas = document.createElement("canvas");
var ctx = canvas.getContext("2d");
var canvasCopy = document.createElement("canvas");
var copyContext = canvasCopy.getContext("2d");
// Create original image
var img = new Image();
img.src = base64;
img.onload = function(){
// Determine new ratio based on max size
var ratio = 1;
if(img.width > maxWidth) {
ratio = maxWidth / img.width;
}
else if(img.height > maxHeight) {
ratio = maxHeight / img.height;
}
// Draw original image in second canvas
canvasCopy.width = img.width;
canvasCopy.height = img.height;
copyContext.drawImage(img, 0, 0);
// Copy and resize second canvas to first canvas
canvas.width = img.width * ratio;
canvas.height = img.height * ratio;
ctx.drawImage(canvasCopy, 0, 0, canvasCopy.width, canvasCopy.height, 0, 0, canvas.width, canvas.height);
alert(canvas.toDataURL());
window['imageDataRes'] = canvas.toDataURL();
SendWhenisReady(window['imageDataRes'])
}
}
I am using HTML5 canvas and putting two images there but I am facing one problem which is, one image is loaded and visible in chrome but another image is only visible in mozilla but after refresh. I don't know why this is happening as I am new in canvas.
var canvas = document.getElementById('canvas');
var context = canvas.getContext('2d');
var x = 0;
var y = 0;
var width = 900;
var height = 700;
var imageObj = new Image();
imageObj.onload = function() {
context.drawImage(imageObj, x, y, width, height);
};
imageObj.src = 'http://img13.deviantart.net/1550/i/2011/353/4/2/mobile_game_background_by_disnie-d4jmr8r.jpg';
var startImageObj = new Image();
startImageObj.onload = function() {
context.drawImage(startImageObj, (canvas.width-startImageObj.width)/2, (canvas.height-startImageObj.height)/2)
};
startImageObj.src = 'http://assets.halfbrick.com/yc/v3/images/button-play.png';
<canvas id="canvas" width="900" height="700"></canvas>
fiddle
As onload event is asynchronous, make sure play-button is being set in the onload-handler of the base-image
var canvas = document.getElementById('canvas');
var context = canvas.getContext('2d');
var x = 0;
var y = 0;
var width = 900;
var height = 700;
var imageObj = new Image();
imageObj.onload = function() {
context.drawImage(imageObj, x, y, width, height);
var startImageObj = new Image();
startImageObj.onload = function() {
context.drawImage(startImageObj, (canvas.width - startImageObj.width) / 2, (canvas.height - startImageObj.height) / 2)
};
startImageObj.src = 'https://d30y9cdsu7xlg0.cloudfront.net/png/5670-200.png';
};
imageObj.src = 'http://img13.deviantart.net/1550/i/2011/353/4/2/mobile_game_background_by_disnie-d4jmr8r.jpg';
<canvas id="canvas" width="900" height="700"></canvas>
#Rayon's answer is right in that with your current implementation you can't know which image will have loaded first, but IMO it is wrong to wrap everything in the same callback, since you will have to wait for the first image has loaded before trigger the loading of the next one, whcih will produce a flicker apparition of the last image.
Instead, create a preloader function that will trigger a drawing function when both images have been loaded.
This has the advantage to make it easier to call your drawing function later, and also to keep your code a bit cleaner :
/* preloader
inputs :
an array containing the urls of the images to load,
a callback function called when all the images have loaded
outputs:
an array containing all the image elements in the same order has the urls where provided
*/
function preloader(imageURLs, callback) {
var toLoad = imageURLs.length,
toReturn = [],
decrement = function() {
if (--toLoad <= 0) {
callback();
}
};
imageURLs.forEach(function(url) {
var img = new Image()
// you may want to include a more verbose error function
img.onload = img.onerror = decrement;
img.src = url;
toReturn.push(img);
});
return toReturn;
}
function draw() {
ctx.drawImage(background, 0, 0, canvas.width, canvas.height);
ctx.drawImage(front, (canvas.width - front.width) / 2, (canvas.height - front.height) / 2);
}
var ctx = canvas.getContext('2d'),
urls = [
'http://img13.deviantart.net/1550/i/2011/353/4/2/mobile_game_background_by_disnie-d4jmr8r.jpg',
'https://d30y9cdsu7xlg0.cloudfront.net/png/5670-200.png'
],
images = preloader(urls, draw),
background = images[0],
front = images[1];
<canvas id="canvas" width="900" height="700"></canvas>
Using createElement on canvas as a temporary placement to draw shapes and encounter an error. Does the canvas have to be visible before convert to image or what is the solution to create canvas and flatten into an image?
var ctx = document.createElement("canvas");
ctx.width = 200;
ctx.height = 100;
var ctx1 = ctx.getContext("2d");
var tan30=Math.tan( 0.1 );
ctx1.setTransform(1, tan30, 0,1,0,0);
ctx1.lineWidth = 10;
ctx1.beginPath();
ctx1.moveTo(0, 0);
ctx1.lineTo(200, 0);
ctx1.stroke();
var asd=convertCanvasToImage(ctx1)
console.log(asd)
function convertCanvasToImage(aaa) {
var image = new Image();
image.src = aaa[0].toDataURL("image/png");
return image;
}
Your code reads like this
var ctx = document.createElement("canvas");
var ctx1 = ctx.getContext("2d");
var asd = convertCanvasToImage(ctx1)
function convertCanvasToImage(aaa) {
something = aaa[0].toDataURL("image/png");
}
see how you pass the context to the function, so really you're doing
var ctx = document.createElement("canvas");
var ctx1 = ctx.getContext("2d");
var asd = ctx1[0].toDataURL("image/png");
However, the context is not an array, and toDataURL is a method on the canvas element, not the context, so you should be doing
var ctx = document.createElement("canvas");
ctx.width = 200;
ctx.height = 100;
var ctx1 = ctx.getContext("2d");
var tan30=Math.tan( 0.1 );
ctx1.setTransform(1, tan30, 0,1,0,0);
ctx1.lineWidth = 10;
ctx1.beginPath();
ctx1.moveTo(0, 0);
ctx1.lineTo(200, 0);
ctx1.stroke();
var asd = convertCanvasToImage(ctx)
console.log(asd)
function convertCanvasToImage(aaa) {
var image = new Image();
image.src = aaa.toDataURL("image/png");
return image;
}
Where you pass the canvas into the function, and access it's toDataURL method directly.
The argument isn't really neccessary either, as the default type is actually "image/png", and you only have to pass in an argument if you something other than png.
FIDDLE
i am resizing an image using canvas and javascript... After the image has finished, I want to do some logic. Is there an event for finished? I tried onloadend but it never gets called:
var fr = new FileReader();
fr.onload = function (e) {
var img = new Image();
img.onloadend = function() {
console.log('finished logic here');
}
img.onload = function(){
var MAXWidthHeight = 488;
var ratio = MAXWidthHeight / Math.max(this.width,this.height);
var w = this.width * ratio;
var h = this.height * ratio;
var c = document.createElement("canvas");
c.width = w;
c.height = h;
c.getContext("2d").drawImage(this,0,0,w,h);
this.src = c.toDataURL();
document.body.appendChild(this);
}
img.src = e.target.result;
}
fr.readAsDataURL(files[i]);
An image is loaded asynchronously, but...
All processing inside .onload is synchronous, so you could just add a callback like this:
img.onload = function(){
var MAXWidthHeight = 488;
var ratio = MAXWidthHeight / Math.max(this.width,this.height);
var w = this.width * ratio;
var h = this.height * ratio;
var c = document.createElement("canvas");
c.width = w;
c.height = h;
c.getContext("2d").drawImage(this,0,0,w,h);
this.src = c.toDataURL();
document.body.appendChild(this);
// after this img has been appended, execute myCallback()
myCallback();
}
function myCallback(){
console.log("I'm called after this img has been appended");
}
If you load multiple images, you will have multiple .onloads and therefore you will have myCallback executed multiple times. If you just want myCallback executed once after all images have been appended, you would set a countdown variable that delays calling myCallback until all images have been appended.
var countdown=imageCount; // imageCount is the total count of images to be processed
// and then in .onload
if(--countdown==0){
myCallback();
}