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();
}
Related
I'm trying to resize an image in the client side and then send it to my server. But the image is not all the times settled correctly to the canvas used to resize the image.
I already sent the image resize but i need to send it at least 2 times to work.
I put <p> labels in my html to verify the data of the image and i can see the data incomplete the first time i send it.
This is my html
function ResizeImage() {
var filesToUpload = document.getElementById('imageFile').files;
var file = filesToUpload[0];
console.log('Data');
// Create an image
var img = document.createElement("img");
// Create a file reader
var reader = new FileReader();
// Set the image once loaded into file reader
reader.onload = function(e) {
//HERE IN THIS PART, the e.target.result works strange
img.src = e.target.result;
var canvas = document.createElement("canvas");
//var canvas = $("<canvas>", {"id":"testing"})[0];
var ctx = canvas.getContext("2d");
ctx.drawImage(img, 0, 0);
// var MAX_WIDTH = 400;
// var MAX_HEIGHT = 400;
var width = 200;
var height = 200;
if (img.width > img.height) {
if (img.width > width) {
height *= height / img.width;
//width = width;
}
} else {
if (img.height > height) {
width *= height / img.height;
//height = height;
}
}
canvas.width = width;
canvas.height = height;
ctx = canvas.getContext("2d");
ctx.drawImage(img, 0, 0, width, height);
var dataurl = canvas.toDataURL("image/png");
console.log(dataurl);
canvas.toBlob((blob) => {
var fd = new FormData();
fd.append("name", "paul");
fd.append("image", blob);
fd.append("key", "××××××××××××");
var xhr = new XMLHttpRequest();
xhr.open("POST", "http://localhost:5000/2");
xhr.send(fd);
}, "image/png", 1)
document.getElementById('output').src = dataurl;
var para = document.createElement("p");
var node = document.createTextNode(dataurl);
para.appendChild(node);
var element = document.getElementById("contenedor");
element.appendChild(para);
}
// Load files into file reader
reader.readAsDataURL(file);
}
<input type="text" name="fileName">
<input type="file" id="imageFile" name="sampleFile" accept="image/png" />
<input type='button' value='Upload!' onclick="ResizeImage()" />
<img src="" id="output">
<div id="contenedor"></div>
<hr>
I expect it works the first time i send the data to the server.
Img.src is asynchronous.
To verify this, replace 'img.src = e.target.result;' with
img.onload = function () { console.log('done')}
img.src = e.target.result;
console.log('src')
If done runs after src, you know that the image isn't loaded yet when you try to ctx.drawImage.
To fix, simply move all your resizing code into img.onload.
I have a little problem with a loop. I am building a little tool, where a user must upload 12 images. The images are cropped in rectangles and placed on buttons. I am almost ready, but somehow the loop doesn't work well. All images land on the last button. Maybe something wrong in the loop here?
JS/JQuery:
for (var i = 0; i < 12; i++) {
var j=i+1;
var reader = new FileReader();
reader.onload = function (e) {
var img = new Image();
img.src = e.target.result;
img.onload = function () {
var getimage= '#getimage'+j;
// CREATE A CANVAS ELEMENT AND ASSIGN THE IMAGES TO IT.
var canvas = document.createElement("canvas");
var ctx = canvas.getContext("2d");
ctx.clearRect(0, 0, canvas.width, canvas.height)
var posh, posw;
var factheight=img.height;
var factwidth=img.width;
if(factwidth<factheight){
canvas.width = img.width;
canvas.height= img.width;
posh=(img.height-img.width)/2;
posw=0;
}
else if(factheight<factwidth){
canvas.height = img.height;
canvas.width = img.height;
posh=0;
posw=(img.width-img.height)/2;
}
else{
canvas.width = img.width;
canvas.height= img.height;
posh=0;
posw=0;
}
ctx.drawImage(img, posw, posh, canvas.width, canvas.height, 0, 0, canvas.width, canvas.height);
var cropped=canvas.toDataURL("image/png");
$(getimage).attr("src",cropped); // SHOW THE IMAGES OF THE BROWSER.
}
}
reader.readAsDataURL($('.multiupload')[0].files[i]);
}
Here is also a link to the JSFiddle. Appreciate your help, since I don't know exactly how reader.readAsDataURL($('.multiupload')[0].files[i]); and target.result works
I'm guessing that your loop has finished before any of the images are fully loaded so j will be 11 before its used to find the relevant button. Try changing
img.onload = function () { .... }
to
img.onload = myFunction(id)
Then move everything out of the inline function into its own function with an input parameter. Then pass j as the id param.
I've done an example for you. As I answered in comments
var reader = new FileReader();
reader.onload = (function(j){return function (e) {
var img = new Image();
...
https://jsfiddle.net/ykze3f9r/
The main issue with the code was the j variable. It was always set to the last number because of the way for loops work. You have to instead bind that number. I broke up into separate functions to make it easier to read. Here's the working JSFiddler: https://jsfiddle.net/eh6pr7ee/2/
Processes the image...
var processImg = function( img, imgNum ) {
var getimage= '#getimage' + imgNum;
// CREATE A CANVAS ELEMENT AND ASSIGN THE IMAGES TO IT.
var canvas = document.createElement("canvas");
var ctx = canvas.getContext("2d");
ctx.clearRect(0, 0, canvas.width, canvas.height)
var posh, posw;
var factheight = img.height;
var factwidth = img.width;
if (factwidth < factheight) {
canvas.width = img.width;
canvas.height = img.width;
posh = (img.height-img.width)/2;
posw = 0;
}
else if (factheight < factwidth) {
canvas.height = img.height;
canvas.width = img.height;
posh = 0;
posw = (img.width-img.height)/2;
}
else {
canvas.width = img.width;
canvas.height= img.height;
posh = 0;
posw = 0;
}
ctx.drawImage(img, posw, posh, canvas.width, canvas.height, 0, 0, canvas.width, canvas.height);
var cropped = canvas.toDataURL("image/png");
$(getimage).attr("src",cropped); // SHOW THE IMAGES OF THE BROWSER.
};
Creates image and sets source...
var setImage = function( imgNum, e ) {
var img = new Image();
img.src = e.target.result;
img.onload = processImg.bind( this, img, imgNum );
};
Create a handler function for image uploads...
var handleImageUploads = function() {
if (parseInt($(this).get(0).files.length) > 12 || parseInt($(this).get(0).files.length) < 12) {
alert("Please upload 12 photos");
}
else {
//loop for each file selected for uploaded.
for (var i = 0; i < 12; i++) {
var reader = new FileReader();
reader.onload = setImage.bind( this, i+1 );
reader.readAsDataURL($('.multiupload')[0].files[i]);
} // for
console.log("done");
$('body').removeClass("loading");
}; // else
}
Binds the handler function.
$('.multiupload').on("change", handleImageUploads);
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?
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>
I'm working on a concent to show the thumbnail of a large number of images from the local drive.
With HTML5 File API is seems quite possible, but whn I try to load a big number of images the memory usage of the browser goes over the roof and the collapses.
I think the problem is that the FileReader doesn't releases the memory after a file read.
Originally I had a new instance of FileReader and a simple loop to iterate through the images.
To solve this memory problem I replaced this to have one FileReader only, but it did not really help.
Here is the relevand code block:
<script>
var areader = new FileReader();
var counter=0;
function loadImage(file) {
var canvas = document.createElement("canvas");
areader.onload = function (event) {
var img = new Image;
img.onload = function () {
canvas.width = img.width / 100;
canvas.height = img.height / 100;
var ctx = canvas.getContext("2d");
ctx.drawImage(img, 0, 0, img.width / 100, img.height / 100);
var browse = document.getElementById("uploadInput");
if (browse.files.length > counter) {
counter++;
areader.result = null;//I don't think this makes any difference
loadImage(browse.files[counter]);
}
};
img.src = event.target.result;
};
areader.readAsDataURL(file);
preview.appendChild(canvas);
}
function showImages() {
loadImage(document.getElementById("uploadInput").files[0]);
}
If anybody come across this problem the I do something very stupid could you reply.
Thanks,
Tamas
It's not the file reader but you are using the entire image's data in base64 as the src property of the image, which will actually take 133% of the image's size in memory.
You should use Blob URLs instead:
var URL = window.URL || window.webkitURL;
function loadImage( file ) {
var canvas = document.createElement("canvas"),
img = new Image();
img.onload = function() {
canvas.width = img.width / 100;
canvas.height = img.height / 100;
var ctx = canvas.getContext("2d");
ctx.drawImage(img, 0, 0, img.width / 100, img.height / 100);
URL.revokeObjectURL( img.src );
img = null;
var browse = document.getElementById("uploadInput");
if (browse.files.length > counter) {
counter++;
loadImage(browse.files[counter]);
}
};
img.src = URL.createObjectURL( file );
preview.appendChild(canvas);
}