HTML Canvas Will Not Draw Image - javascript

Okay, I know that there are loads of subjects that look identical to this one on SO, but none of them have fixed my issue...
I'm trying to grab an image from a file input and throw it onto a canvas so that I can later turn it into a base-64 image... But I've hit a snag in the process that I was not expecting, in drawing the image to the canvas...
Taking the following HTML:
<input type="file" id="fileIn" onchange="preview()"><br>
<img id="filePreview"><br>
<canvas id="fileCanvas"></canvas>
And the following script:
var dataurl = '';
function preview(){
document.getElementById('filePreview').src = URL.createObjectURL(document.getElementById('fileIn').files[0]);
document.getElementById('filePreview').onload = showInCanvas;
cnv = document.getElementById('fileCanvas');
ctx = cnv.getContext('2d');
}
function showInCanvas(){
cnv.style.width = document.getElementById('filePreview').naturalWidth + 'px';
cnv.width = document.getElementById('filePreview').naturalWidth;
cnv.style.height = document.getElementById('filePreview').naturalHeight + 'px';
cnv.height = document.getElementById('filePreview').naturalHeight + 'px';
ctx.clearRect(0, 0, cnv.width, cnv.height);
ctx.drawImage(document.getElementById('filePreview'), 0, 0);
dataurl = cnv.toDataURL('image/png');
}
The problem I'm having is that the image simply refuses to draw onto the canvas. Even going into the console and drawing the image to the canvas manually, after the script has run, it still refuses to draw. Because of this, the image data simply runs through as data:,
It's an https site, if that affects anything.
For clarification, here's my question:
Why is the canvas refusing to render this image? How can I fix this issue?

If the intent is to convert the image to Data-URI ("base64"), the FileReader can be used - no need for canvas (which can also alter the image/final compression):
fileIn.onchange = function(e) {
var fr = new FileReader();
fr.onload = done.bind(fr);
fr.readAsDataURL(e.target.files[0]);
}
function done() {
var dataURI = filePreview.src = this.result;
alert(dataURI.substr(0, 35) + "...")
}
<input type="file" id="fileIn"><br>
<img id="filePreview"><br>
<canvas id="fileCanvas"></canvas>

Related

Overlay two base64 image url and save them as single image in Javascript

I have a whiteboard option during a video call in my web application built in angularjs (1.x). Users can draw above the video in a canvas element. I need to take a screenshot of the current video position and drawing on the canvas. I am able to get the video frame as base64 URL(second image) and canvas drawing as base64 URL(third image) separately. But I need to get it as a combined single image in base64 URL like the first image.
HTML
<img id="img1">
<img id="img2">
<canvas id="mergedImage" width="382" height="510"></canvas>
JS
var drawingImg1 = document.getElementById('img1');
drawingImg1.setAttribute('src', img1);
var drawingImg2 = document.getElementById('img2');
drawingImg2.setAttribute('src', img2);
var c = document.getElementById('mergedImage');
var width = 382;
var height = 510;
var ctx = c.getContext("2d");
var imageObj2 = new Image();
imageObj2.onload = function (){
ctx.drawImage(imageObj2, 0, 0, width, height);
var imageObj1 = new Image();
imageObj1.onload = function (){
imageObj1.style.objectFit = "cover";
ctx.drawImage(imageObj1, 0, 0,width, height);
};
imageObj1.src = img2;
};
imageObj2.src = img1;
I tried the above code and it gives the output as video frame image only not the drawing included.
I want the output as the first image. Please, someone, guide me to do this.
I have created a jsfiddle here
The following solution worked for me to overlap 2 images and got a single image in a canvas.
Link

How to wait for the multiple images to load in the canvas and convert to base64?

I'm trying to get a base64 version of a canvas in HTML5.
Currently, the base64 image that I get from the canvas is blank.
I have found similar questions for example this one:
HTML Canvas image to Base64 problem
However, the issue that I have is that because I am adding 2 images in the canvas, I cannot use the example provided in those answers as most of them are using single image.
I understand that I have to wait until the canvas image is loaded properly before trying to get the base64 image. But I don't know how in my case.
This is my code:
var canvas = document.getElementById('myCanvas');
var context = canvas.getContext('2d');
context.canvas.width = 1000;
context.canvas.height = 1000;
var imageObj1 = new Image();
imageObj1.src = "http://www.offthegridnews.com/wp-content/uploads/2017/01/selfie-psuDOTedu.jpg";
imageObj1.onload = function() {
context.drawImage(imageObj1, 0, 180, canvas.width, canvas.height);
};
var imageObj2 = new Image();
imageObj2.src = "http://www.publicdomainpictures.net/pictures/150000/velka/banner-header-tapete-145002399028x.jpg"
imageObj2.onload = function() {
context.drawImage(imageObj2, 0, 0, canvas.width, 180);
};
// get png data url
//var pngUrl = canvas.toDataURL();
var pngUrl = canvas.toDataURL('image/png');
// get jpeg data url
var jpegUrl = canvas.toDataURL('image/jpeg');
$('#base').val(pngUrl);
<div class="contents" style="overflow-x:hidden; overflow-y:scroll;">
<div style="width:100%; height:90%;">
<canvas id="myCanvas" class="snap" style="width:100%; height:100%;" onclick="takephoto()"></canvas>
</div>
</div>
<p>
This is the base64 image
</p>
<textarea id="base">
</textarea>
and this is a working FIDDLE:
https://jsfiddle.net/3p3e6Ldu/1/
Can someone please advice on this issue?
Thanks in advance.
EDIT:
As suggested in the comments bellow, i tried to use a counter and when the counter reaches a specific number, I convert the canvas to base64.
Like so:https://jsfiddle.net/3p3e6Ldu/4/
In both your examples (the one from the question and the one from the comments), the order of commands does not really respect the async nature of the task at hand.
In your later example, you should put the if( count == 2 ) block inside the onload callbacks to make it work.
However, even then you will run into the next problem: You are loading the images from different domains. You can still draw them (either into the canvas or using an <img> tag), but you are not able to access their contents directly. Not even with the detour of using the <canvas> element.
I changed to code so it would work, if the images are hosted on the same domain. I also used a function to load the image and promises to handle the callbacks. The direct way of using callbacks and a counting variable, seem error-prone to me. If you check out the respective fiddle, you will notice the SecurityError shown. This is the result of the aforementioned problem with the Same-Origin-Policy I mentioned.
A previous question of mine in a similar direction was about how to detect, if I can still read the contents of a <canvas> after adding some images.
const canvas = document.getElementById('myCanvas');
const context = canvas.getContext('2d');
context.canvas.width = 1000;
context.canvas.height = 1000;
// function to retrieve an image
function loadImage(url) {
return new Promise((fulfill, reject) => {
let imageObj = new Image();
imageObj.onload = () => fulfill(imageObj);
imageObj.src = url;
});
}
// get images
Promise.all([
loadImage("http://www.offthegridnews.com/wp-content/uploads/2017/01/selfie-psuDOTedu.jpg"),
loadImage("http://www.publicdomainpictures.net/pictures/150000/velka/banner-header-tapete-145002399028x.jpg"),
])
.then((images) => {
// draw images to canvas
context.drawImage(images[0], 0, 180, canvas.width, canvas.height);
context.drawImage(images[1], 0, 0, canvas.width, 180);
// export to png/jpg
const pngUrl = canvas.toDataURL('image/png');
const jpegUrl = canvas.toDataURL('image/jpeg');
// show in textarea
$('#base').val(pngUrl);
})
.catch( (e) => alert(e) );

Javascript: Simulate cropping and get file image fro memory

I upload an image using a file input:
<input type="file" id="product_images-0"/>
Then I get the file with javascript:
var element = document.getElementById('product_images-1');
console.log(element.files[0]);
I want to get the image file from memory, show it to user, simulate an image crop to get the coordinates and show the "cropped-simulated" image to the user, by using css transform/translate.
After cropping if the user doesn't like the image can remove it.
It is possible. I don't want to use Ajax because the user can remove an image before submits, and deleting this "remove" image from backend is hard to do it.
Please see simple proof-of-concept below. Once file is submitted you need to create new HTMLImageElement and append it to your preview. Then you can draw cropped image on canvas using drawImage.
Remember to validate if file you are submitting is image type!
var fileInput = document.getElementById('product_images-0');
var preview = document.querySelector('div.preview');
var canvas = document.getElementById('canvas');
var ctx = canvas.getContext('2d');
fileInput.onchange = function (event) {
var curFiles = fileInput.files;
if (curFiles.length > 0) {
while (preview.firstChild) {
preview.removeChild(preview.firstChild);
}
var image = document.createElement('img');
image.src = window.URL.createObjectURL(curFiles[0]);
preview.appendChild(image);
image.onload = function () {
ctx.drawImage(image, 0.2 * image.width, 0.2 * image.height, 0.5 * image.width, 0.5 * image.height, 0, 0, canvas.width, canvas.height);
};
}
};
<input type="file" id="product_images-0"/>
<div class="preview">
<p>No files currently selected for upload</p>
</div>
<canvas id="canvas" width="200" height="200"></canvas>

Convert SVG dataUrl to base64

I'm using a plugin (dom-to-image) to generate a SVG content from a div.
It returns me a dataURL like this:
data image/xml, charset utf-8, <svg...
If a put this on a <img src the image is shown to normally.
The intent is to grab this dataURL, convert it to base64 so I can save it as an image.png on a mobile app.
Is it possible?
I tryied this solution https://stackoverflow.com/a/28450879/1691609
But coudn't get to work.
The console fire an error about the dataUrl
TypeError: Failed to execute 'serializeToString' on 'XMLSerializer': parameter 1 is not of type 'Node'.
==== UPDATE :: PROBLEM EXPLANATION/HISTORY ====
I'm using Ionic Framework, so my project is an mobile app.
The dom-to-image is already working cause right now, its rendering a PNG through toPng function.
The problem is the raster PNG is a blurry.
So I thought: Maybe the SVG will have better quality.
And it IS!! Its 100% perfect, actually.
On Ionic, I'm using 2 step procedure to save the image.
After get the PNG generated by the dom-to-img(base64) dataURL, I convert it to a Blob and then save into device.
This is working, but the final result, as I said, is blurry.
Then with SVG maybe it will be more "high quality" per say.
So, in order to do "minimal" change on a process that s already working :D I just need to convert an SVG into base64 dataURL....
Or, as some of you explained to me, into something else, like canvas...
I don't know any much :/
===
Sorry for the long post, and I really, really thank your help guys!!
EDIT COUPLE OF YARS LATER
Use JS fiddle for a working example: https://jsfiddle.net/msb42ojx/
Note, if you don't own DOM content (images), and those images don't have CORS enabled for everyone (Access-Control-Allow-Origin header), canvas cant render those images
I'm not trying to find out why is your case not working, here is how I did when I had something similar to do:
get the image sourcce (dom-to-image result)
set up a canvas with that image inside (using the image source)
download the image from canvas in whatever image you like: png, jpeg whatever
by the way you can resize the image to a standard format
document.getElementById('mydownload').onclick= function(){
var wrapper = document.getElementById('wrapper');
//dom to image
domtoimage.toSvg(wrapper).then(function (svgDataUrl) {
//download function
downloadPNGFromAnyImageSrc(svgDataUrl);
});
}
function downloadPNGFromAnyImageSrc(src)
{
//recreate the image with src recieved
var img = new Image;
//when image loaded (to know width and height)
img.onload = function(){
//drow image inside a canvas
var canvas = convertImageToCanvas(img);
//get image/png from convas
var pngImage = convertCanvasToImage(canvas);
//download
var anchor = document.createElement('a');
anchor.setAttribute('href', pngImage.src);
anchor.setAttribute('download', 'image.png');
anchor.click();
};
img.src = src;
// Converts image to canvas; returns new canvas element
function convertImageToCanvas(image) {
var canvas = document.createElement("canvas");
canvas.width = image.width;
canvas.height = image.height;
canvas.getContext("2d").drawImage(image, 0, 0);
return canvas;
}
// Converts canvas to an image
function convertCanvasToImage(canvas) {
var image = new Image();
image.src = canvas.toDataURL("image/png");
return image;
}
}
#wrapper{
background: red;
color: blue;
}
<script src="https://rawgit.com/tsayen/dom-to-image/master/src/dom-to-image.js"></script>
<button id='mydownload'>Download DomToImage</button>
<div id="wrapper">
<img src="http://i.imgur.com/6GvKdxY.jpg"/>
<div> DUDE IS WORKING</div>
<img src="http://i.imgur.com/6GvKdxY.jpg"/>
</div>
I translated #SilentTremor's solution into React/JS-Class:
class SVGToPNG {
static convert = function (src) {
var img = new Image();
img.onload = function () {
var canvas = SVGToPNG.#convertImageToCanvas(img);
var pngImage = SVGToPNG.#convertCanvasToImage(canvas);
var anchor = document.createElement("a");
anchor.setAttribute("href", pngImage.src);
anchor.setAttribute("download", "image.png");
anchor.click();
};
img.src = src;
};
static #convertImageToCanvas = function (image) {
var canvas = document.createElement("canvas");
canvas.width = image.width;
canvas.height = image.height;
canvas.getContext("2d").drawImage(image, 0, 0);
return canvas;
};
static #convertCanvasToImage = function (canvas) {
var image = new Image();
image.src = canvas.toDataURL("image/png");
return image;
};
}
export default SVGToPNG;
Usage:
let dataUrl = someCanvas.toDataURL("image/svg+xml");
SVGToPNG.convert(dataUrl);

FileReader on safari firing to soon

I'm working on a multi part html questionnaire style form that has a lot of text questions along with a few images. On questions that are images the user is selects the image, i create a canvas element and display the resized image in it underneath the file input.
if (window.FileReader)
{
var file = element.files[0];
var $input = $(element);
var $fileName = file.name;
var reader = new FileReader();
reader.onload = function(e) {
var img = document.createElement("img");
img.src = e.target.result;
var dataID = $input.data("questionId");
var canvasID = "canvas_" + dataID;
$("#"+canvasID).remove();
var canvas = document.createElement("canvas");
canvas.setAttribute("id", canvasID);
canvas.setAttribute("height", "200");
canvas.setAttribute("width", "200");
var heightWidth = getHeightWidth(img);
canvas.height = heightWidth[0];
canvas.width = heightWidth[1];
var ctx = canvas.getContext("2d");
ctx.drawImage(img,0,0, canvas.width, canvas.height);
var sectionID = "section_" + dataID;
$("#" + sectionID).append(canvas);
$("#file_title-" + dataID).val($fileName);
}
reader.readAsDataURL(file);
}
else
{
alert("This browser does not support image uploading.");
}
This works fine in chrome but not in safari (safari 8.x) on desktop or iOS. The problem in my code is that on Safari it returns height=0 width=0 from getHeightWidth() which gives me think the img isn't ready to be handled yet. This theory is further validated because if i change to a new picture and change back to the original it displays properly.
I'm really not sure where to start, any help debugging this would be greatly appreciated. Thanks everyone
The code assumes the image loading is synchronous, but it's asynchronous and should be assumed so even with data-URIs. If the image hasn't loaded properly its width and height attributes will be 0.
You can solve this by adding an onload handler for img, then move the code for detecting and setting size inside that handler (remember also to add an onerror handler as well in case the image file is corrupted).

Categories

Resources