I am making a photo editor, where you have the option to upload an image that will be drawn on a canvas. I let the user upload a new image without refreshing the website. However, the second time you try to upload a different image, the whole image won't be drawn on the canvas. The only part that will be drawn is the size of the image that the user uploaded before that. This is pretty weird as a new size is set every time the user uploads a new image.
After some trial and error, I found out that the new canvas size is actually set to the size of the image that is uploaded. However, it still doesn't draw the whole image, even though it is specified that the image should be drawn at the size of the canvas. Therefore, it is a problem lying in the line that draws the image, and not the part that sets the size of canvas. I tried drawing the image twice instead of once just to see what would happen. It actually worked, even though the the two lines for drawing the image are identical. I found this pretty strange, and I feel like this is not the right way to fix it.
Here is the part of the code that loads the image:
var imageLoader = document.getElementById('imageLoader');
imageLoader.addEventListener('change', handleImage, false);
var canvas = document.getElementById('imageCanvas');
var ctx = canvas.getContext('2d');
var img;
var ratio;
var overlay = document.getElementById("overlay")
canvas.width = 400;
function handleImage(e){
var reader = new FileReader();
reader.onload = function(event){
img = new Image();
img.onload = function(){
ratio = this.height / this.width;
canvas.height = canvas.width * ratio;
ctx.drawImage(img,0,0,canvas.width, canvas.height);
}
overlay.style = "display: none;"
img.src = event.target.result;
window.onbeforeunload = function() {
return true;
};
}
reader.readAsDataURL(e.target.files[0]);
}
canvas { border: 1px solid; }
<input type="file" id="imageLoader">
<canvas id="imageCanvas"></canvas>
<div id="overlay">Unrelated overlay...</div>
The behavior you're getting is not weird but makes perfect sense because you’re supposed to adjust the size of the canvas to the NATURAL dimesnions of the image loaded each time!
What makes you think that the canvas automatically adjusts itself to any image loaded?
You need to do this with every new image load...
canvas.width=img.naturalWidth;
canvas.height=img.naturalHeight;
canvas.style.transform='scale(0.5,0.5)';
After some time, I found out that I had to use clearRect before redrawing the image.
Now the JavaScript code looks like this:
var imageLoader = document.getElementById('imageLoader');
imageLoader.addEventListener('change', handleImage, false);
var canvas = document.getElementById('imageCanvas');
var ctx = canvas.getContext('2d');
var img;
var ratio;
var overlay = document.getElementById("overlay")
canvas.width = 400;
function handleImage(e){
var reader = new FileReader();
reader.onload = function(event){
img = new Image();
img.onload = function(){
ratio = this.height / this.width;
canvas.height = canvas.width * ratio;
ctx.clearRect(0, 0, canvas.width, canvas.height);
ctx.drawImage(img,0,0,canvas.width, canvas.height);
}
overlay.style = "display: none;"
img.src = event.target.result;
window.onbeforeunload = function() {
return true;
};
}
reader.readAsDataURL(e.target.files[0]);
}
Related
Converting image to the canvas with canvas.drawImage fails(but not throws err), the canvas looks transparent empty image
It doesn't happen for all image files I could not figure out which property of the image causes this but I uploaded an example photo with this issue
I started having this problem with chrome v83-v84
firefox works fine.
I want to upload a specified photo (its inside fiddle) and want to make canvas and draw canvas from the uploaded image and display it on chrome (chrome version should bigger or equal v85)
function testimg(src) {
var img = document.createElement("img");
img.src = src;
document.querySelector('#test').appendChild(img)
}
$('#file').on('change', fileAdded)
function fileAdded(event) {
var reader = new FileReader;
reader.onload = function () {
var image = new Image();
image.src = reader.result;
image.onload = function () {
document.querySelector('#test').appendChild(this)
var imgToSend = processImg(this, this.width, this.height);
testimg(imgToSend)
};
};
reader.readAsDataURL(event.currentTarget.files[0]);
}
function processImg(image, srcWidth, srcHeight) {
var canvas = document.createElement('canvas');
canvas.width = srcWidth;
canvas.height = srcHeight;
var ctx = canvas.getContext("2d");
ctx.drawImage(image, 0, 0);
ctx.fill();
return canvas.toDataURL();
}
https://jsfiddle.net/msacar/2Lgmc85t/24/
I have a problem loading an image to a canvas from a file (image) input. The fileInput object is the image picker. This is all one block JS file on my site:
<script>
function picEditor() {
var fileInput = document.createElement("input");
fileInput.setAttribute("type", "file");
fileInput.setAttribute("accept", "image/png");
fileInput.setAttribute("id", "fluff");
document.body.appendChild(fileInput);
Second part, the image is created. The image and load button are created first. The button's click function is supposed to set the source of the image to that of the file input's blob URL, then draw the image on the canvas.
var img = document.createElement("img");
var loadButton = document.createElement("button");
loadButton.onclick = function() {
img.src = window.URL.createObjectURL(fileInput.files[0]);
ctx.drawImage(img, 0, 0, canvas.width, canvas.height);
};
loadButton.innerHTML = "Click here";
document.body.appendChild(loadButton);
Third part, the canvas and rendering context are created.
var canvas = document.createElement("canvas");
canvas.style.position = "absolute";
canvas.style.left = "740px";
canvas.style.top = "15px";
canvas.style.border = "1px #fa0 solid";
canvas.width = 600;
canvas.height = 600;
document.body.appendChild(canvas);
var ctx = canvas.getContext("2d");
}
window.onload = picEditor();
</script>
My three visible objects are getting drawn, but the image isn't getting drawn when an image has been inputted and the load button has been clicked.
You have to wait till the image is ready before drawing it on the canvas
loadButton.onclick = function() {
img.onload = function() {
ctx.drawImage(this, 0, 0, canvas.width, canvas.height);
};
img.src = window.URL.createObjectURL(fileInput.files[0]);
};
I have two main questions. I'm trying to resize a base64 image according to the answer in
JavaScript reduce the size and quality of image with based64 encoded code
This seemed simple enough, except for the fact that I need to get only the base64 string from that function, without the data:image... header.
I tried to modify the function like this:
function resizeBase64Img(base64, width, height) {
var canvas = document.createElement("canvas");
canvas.width = width;
canvas.height = height;
var context = canvas.getContext("2d");
var deferred = $.Deferred();
$("<img/>").attr("src", "data:image/gif;base64," + base64).load(function () {
context.scale(width / this.width, height / this.height);
context.drawImage(this, 0, 0);
deferred.resolve($("<img/>").attr("src", canvas.toDataURL()));
});
var result = canvas.toDataURL();
return result.substr(result.indexOf(",") + 1)
}
which returns a base64 string. This string is corrupted, and it never displays correctly. Why is this happening and how can I fix it?
The other question is, is there a way to resize the image based on percentage and not on pixel size?
Thanks
I ended up rewriting the function based on another canvas function I found. This function takes the src of a base64 img, modifies the size and assigns the result in base64 to another element:
function resizeBase64Img(url, width, height) {
var img = new Image();
img.setAttribute('crossOrigin', 'anonymous');
img.onload = function () {
var canvas = document.createElement("canvas");
canvas.width = width;
canvas.height = height;
var ctx = canvas.getContext("2d");
ctx.scale(width/this.width, height/this.height);
ctx.drawImage(this, 0, 0);
result=canvas.toDataURL();
$('#ASSIGNEDELEMENT').val(result.substr(result.indexOf(",") + 1));
};
img.src = url;
}
And it should be invoked like this:
resizeBase64Img($('#image').attr('src'),300,300);
what I'm trying to do is draw an image to an html canvas using only javascript, and by using the getImageData() function, I'm trying to get the image's bits and then manipulate them in multiple ways.
however, because i'm loading the image using javascript, my suspicion is that the image can't load in time before the call to getImageData(), so the program just stops working when that call is made, as you can see the _log(3) statement isn't being called.
this happens even though I've called getImageData() function inside the images onload() event.
i've already seen a few suggestions in some other stackoverflow threads regarding this issue, but none of them seem to work for me.
any help in the right direction would be really appreciated.
here's the jsfiddle: https://jsfiddle.net/houqdfx9/2/
this is the html:
<p id="log"></p>
this is the javascript:
function _log(string){
document.getElementById("log").innerHTML += string;
}
function _createCanvas(){
var canvas = document.createElement("canvas");
canvas.setAttribute("id", "canvas");
document.body.appendChild(canvas);
return canvas;
}
function _drawAndProcessImage(img, ctx){
ctx.drawImage(img, 0, 0);
_log(img.width + " " + img.height + " ");
var img_data = ctx.getImageData(0,0,img.width,img.height);
_log(3);
/* DO THINGS WITH THE IMG_DATA HERE */
}
function setCanvasAndLoadImage(url, canvas, callback) {
var img = new Image;
var ctx = canvas.getContext("2d");
img.onload = function(){callback(img, ctx);};
img.src = url;
canvas.width = img.naturalWidth;
canvas.height = img.naturalHeight;
};
var canvas = _createCanvas();
setCanvasAndLoadImage("https://pixabay.com/static/uploads/photo/2014/02/01/17/30/apple-256267_960_720.jpg", canvas, _drawAndProcessImage);
Please try this. This works. But, there is a cross domain violation because of the different domains. You would need to proxy this through a server maybe. See this for a solution.
function _log(string){
document.getElementById("log").innerHTML += string;
}
function _createCanvas(){
var canvas = document.createElement("canvas");
canvas.setAttribute("id", "canvas");
document.body.appendChild(canvas);
return canvas;
}
function _drawAndProcessImage(img, ctx){
ctx.drawImage(img, 0, 0);
_log(img.width + " " + img.height + " ");
var img_data = ctx.getImageData(0,0,img.width,img.height);
_log(3);
/* DO THINGS WITH THE IMG_DATA HERE */
}
function setCanvasAndLoadImage(url, canvas, callback) {
var img = new Image;
var ctx = canvas.getContext("2d");
img.onload = function() {
canvas.width = img.naturalWidth;
canvas.height = img.naturalHeight;
callback(img, ctx);
};
img.src = url;
};
var canvas = _createCanvas();
setCanvasAndLoadImage("https://pixabay.com/static/uploads/photo/2014/02/01/17/30/apple-256267_960_720.jpg", canvas, _drawAndProcessImage);
<p id="log"></p>
I think the problem is stems from the lines
canvas.width = img.naturalWidth;
canvas.height = img.naturalHeight;
You are asking for the image dimensions but the image has not been loaded. You have to move that to the image's onload event.
I'm trying to convert a SVG file to PNG by drawing it to canvas.
var svgToCanvas = function(svgElement) {
setNameSpaceOnEl(svgElement);
appendCSSRules(svgElement, getCssRules(svgElement));
var image = new Image();
// this is ugly but will do:
var svgXml = $('<div>').append($(svgElement).clone()).html();
var b64str = btoa(unescape(encodeURIComponent(svgXml)));
// var svgXml = new XMLSerializer().serializeToString(svgElement);
var defer = $q.defer();
image.onload = function() {
var canvas = document.createElement('canvas');
canvas.width = image.width;
canvas.height = image.height;
var context = canvas.getContext('2d');
context.drawImage(image, 0, 0);
defer.resolve(canvas);
};
image.src = 'data:image/svg+xml;base64,' + b64str;
return defer.promise;
};
The problem is that even though the image handling takes place inside the onload function, Firefox yields error NS_ERROR_NOT_AVAILABLE: ... on the line
context.drawImage(image, 0, 0);.
With breakpoints I see the image.src at this point is a valid base64 string, and I can even open up the image itself.
The code above works in Chrome, but not in Firefox. Any suggestions how to get around this in Firefox?