Jump to content

Add image on canvas via user input


Recommended Posts

Hi, I created a simple badge generator app here.

 

I am using the html5 file input and <canvas> to display a badge template and the user has to select the picture.

 

The problem is that sometimes (randomly?), as soon as the image is selected, it doesn't show up in the pixels I designated it to appear in.

 

A quick debug made clear that, when this occurs, the image object is there but it has width 0 and height 0 (see screenshot below).

post-185379-0-93899400-1444650896_thumb.png

Edited by Mad_Griffith
Link to post
Share on other sites

This is the JS I wrote:

(function() {    var badgeImg = new Image();    badgeImg.onload = function() {        var canvas = $('#badge-image')[0],            context = canvas.getContext('2d');        canvas.width = 451;        canvas.height = 300;        drawBaseBadge();        function drawBaseBadge() {            badgeImg.width = '451';            badgeImg.height = '300';            context.drawImage(badgeImg, 0, 0);            clipWidth = 90;            clipHeight = 110;            context.rect(44, 152, clipWidth, clipHeight);            context.clip();        }        function readUploadedImage(file) {            var readFile = new FileReader();            readFile.onload = function(event) {                drawBaseBadge();                profileImgUrl = event.target.result;                profileImg = new Image();                profileImg.id = 'profile-image';                profileImg.src = profileImgUrl;                originalAspectRatio = profileImg.width / profileImg.height;                console.log('Img:', profileImg);                console.log('Original:', profileImg.width, profileImg.height);                profileImg.width = 90;                profileImg.height = profileImg.width / originalAspectRatio;                profileImgPosX = 44;                profileImgPosY = 152;                console.log('Resized:', profileImg.width, profileImg.height);                context.drawImage(profileImg, profileImgPosX - ((profileImg.width - clipWidth) / 2), profileImgPosY - ((profileImg.height - clipHeight) / 2), profileImg.width, profileImg.height);            };            readFile.readAsDataURL(file);        }        $('#badge-uploader input').change(function() {            readUploadedImage(this.files[0]);        });        document.getElementById('badge-download').addEventListener('click', function() {            downloadCanvas(this, 'badge-image', 'myBadge.png');        }, false);        function downloadCanvas(link, canvasId, filename) {            link.href = document.getElementById(canvasId).toDataURL();            link.download = filename;        }    };    badgeImg.src = './img/badge.jpg';}());
Link to post
Share on other sites

Apparently I need to wait until the image width and height are loaded and then add the src. I need to return multiple values from the onload anonymous function and problem is the values doesn't seem to be returned (console.log(result) doesn't work)... what am I doing wrong? See code below

(function() {    var badgeImg = new Image();    badgeImg.onload = function() {        var canvas = $('#badge-image')[0],            context = canvas.getContext('2d');        canvas.width = 451;        canvas.height = 300;        drawBaseBadge();        function drawBaseBadge() {            badgeImg.width = '451';            badgeImg.height = '300';            context.drawImage(badgeImg, 0, 0);            clipWidth = 90;            clipHeight = 110;            context.rect(44, 152, clipWidth, clipHeight);            context.clip();        }        function readUploadedImage(file) {            var readFile = new FileReader();            readFile.onload = function(event) {                drawBaseBadge();                var profileImg = new Image();                profileImg.onload = function(profileImg) {                    profileImg.id = 'profile-image';                    originalAspectRatio = profileImg.width / profileImg.height;                    console.log('Img:', profileImg);                    console.log('Original:', profileImg.width, profileImg.height);                    profileImg.width = 90;                    profileImg.height = profileImg.width / originalAspectRatio;                    profileImgPosX = 44;                    profileImgPosY = 152;                    result = {                        'profileImg.width': profileImg.width,                        'profileImg.height': profileImg.height,                        'profileImgPosX': profileImgPosX,                        'profileImgPosY': profileImgPosY,                        'originalAspectRatio': originalAspectRatio                    };                    profileImg.src = event.target.url;                    console.log(result);            }                    console.log('Resized:', profileImg.width, profileImg.height);                    context.drawImage(profileImg, profileImg.profileImgPosX - ((profileImg.width - clipWidth) / 2), profileImg.profileImgPosY - ((profileImg.height - clipHeight) / 2), profileImg.width, profileImg.height);                readFile.readAsDataURL(file);            }        }        $('#badge-uploader input').change(function() {            readUploadedImage(this.files[0]);        });        document.getElementById('badge-download').addEventListener('click', function() {            downloadCanvas(this, 'badge-image', 'myBadge.png');        }, false);        function downloadCanvas(link, canvasId, filename) {            link.href = document.getElementById(canvasId).toDataURL();            link.download = filename;        }    };    badgeImg.src = './img/badge.jpg';}());
Link to post
Share on other sites

No, you're assigning the src inside the load handler, and the load handler never gets called because the image doesn't get loaded because you don't assign a src to it. Assign the src outside the load handler, not inside. Changing the src inside a load handler isn't a great idea anyway because it's going to immediately fire the load handler again and start a loop.

Link to post
Share on other sites

I understand. I moved it outside the other curly brace, but still it does not work... what else can be wrong?

(function() {var badgeImg = new Image();badgeImg.onload = function() {var canvas = $('#badge-image')[0],context = canvas.getContext('2d');canvas.width = 451;canvas.height = 300;drawBaseBadge();function drawBaseBadge() {badgeImg.width = '451';badgeImg.height = '300';context.drawImage(badgeImg, 0, 0);clipWidth = 90;clipHeight = 110;context.rect(44, 152, clipWidth, clipHeight);context.clip();}function readUploadedImage(file) {var readFile = new FileReader();readFile.onload = function(event) {drawBaseBadge();var profileImg = new Image();profileImg.onload = function(profileImg) {profileImg.id = 'profile-image';originalAspectRatio = profileImg.width / profileImg.height;console.log('Img:', profileImg);console.log('Original:', profileImg.width, profileImg.height);profileImg.width = 90;profileImg.height = profileImg.width / originalAspectRatio;profileImgPosX = 44;profileImgPosY = 152;result = {'profileImg.width': profileImg.width,'profileImg.height': profileImg.height,'profileImgPosX': profileImgPosX,'profileImgPosY': profileImgPosY,'originalAspectRatio': originalAspectRatio};}profileImg.src = event.target.url;console.log(result);console.log('Resized:', profileImg.width, profileImg.height);context.drawImage(profileImg, profileImg.profileImgPosX - ((profileImg.width - clipWidth) / 2), profileImg.profileImgPosY - ((profileImg.height - clipHeight) / 2), profileImg.width, profileImg.height);readFile.readAsDataURL(file);}}$('#badge-uploader input').change(function() {readUploadedImage(this.files[0]);});document.getElementById('badge-download').addEventListener('click', function() {downloadCanvas(this, 'badge-image', 'myBadge.png');}, false);function downloadCanvas(link, canvasId, filename) {link.href = document.getElementById(canvasId).toDataURL();link.download = filename;}};badgeImg.src = './img/badge.jpg';}());
Link to post
Share on other sites

What happens when you run it? Are you checking for error messages? Are you seeing the console.log statements inside the load handler? Note that everything you want to happen after the image loads goes inside the load handler.

Link to post
Share on other sites

If nothing at all is appearing on the console then I would suggest using breakpoints to step through the code. Set breakpoints inside the various load handlers and other functions so that you can pause the code and step through to see what it's doing. You can look at the values of all of the variables while the code is paused and step into or out of functions to trace what it's doing.

Link to post
Share on other sites

This version of readUploadeImage() works in the end...

function readUploadedImage(file) {var readFile = new FileReader();readFile.onload = function (event) {drawBaseBadge();profileImgUrl = event.target.result;profileImg = new Image();profileImg.id = 'profile-image';profileImg.onload = function () {originalAspectRatio = profileImg.width / profileImg.height;console.log('Img:', profileImg);console.log('Original:', profileImg.width, profileImg.height);profileImg.width = 90;profileImg.height = profileImg.width / originalAspectRatio;profileImgPosX = 44;profileImgPosY = 152;console.log('Resized:', profileImg.width, profileImg.height);context.drawImage(profileImg, profileImgPosX - ((profileImg.width - clipWidth) / 2), profileImgPosY - ((profileImg.height - clipHeight) / 2), profileImg.width, profileImg.height);};profileImg.src = profileImgUrl;};readFile.readAsDataURL(file);}
Link to post
Share on other sites

As somebody explained to me:

 

  • [*]The image object is created.
[*]The onload listener is registered. Code in the anonymous function is not yet executed.
[*]src attribut is assigned to the image object.
[*]Image is loaded.
[*]onload event fires and code in the anonymous function is executed.
[*]Calculations are done.
[*]Image is drawn on the canvas.

Link to post
Share on other sites

Join the conversation

You can post now and register later. If you have an account, sign in now to post with your account.

Guest
Reply to this topic...

×   Pasted as rich text.   Paste as plain text instead

  Only 75 emoji are allowed.

×   Your link has been automatically embedded.   Display as a link instead

×   Your previous content has been restored.   Clear editor

×   You cannot paste images directly. Upload or insert images from URL.

Loading...
×
×
  • Create New...