Save Canvas to PNG for iOS - javascript

I use react and html-to-image to export HTML to images. Because Safari not working properly with the jpg/png export, I need to export HTML node to SVG (data url is generated like this : data:image/svg...).
With this string, I am creating a new Image inside a Canvas then saving canvas to .Png with method canvas.toDataURL("image/png").
Everything is working on Firefox/Chrome, but on Safari, it's not working the first time.
First Click - Just avec HTML node (no text)
Second Click - Some Images + Previous HTML Node
Third Click - Everything is working
Here is part of my function :
var canvas = document.createElement("canvas")
var ctx = canvas.getContext('2d')
var img = new Image;
setTimeout(()=>{
htmlToImage.toSvg(document.querySelector('.Banner'), {pixelRatio: RatioVal}).then((dataUrl) => {
canvas.width = 1300*RatioVal;
canvas.height = 690*RatioVal;
img.src = dataUrl;
img.onload = () => {
ctx.drawImage(img,0,0,canvas.width, canvas.height)
var Export = canvas.toDataURL("image/png");
document.body.append(Export)
var a = document.createElement('a');
a.href = Export;
a.download = window.Chara.Nom+".png";
a.click();
};
})
},600)
Do you know why I need to click on Export 3 times to have a full image ?
Is this the best way to convert SVG dataUrl to PNG Image ?
I tried many things (Blob to Png etc..) but the problem is the same on Safari.

Related

"Silently" save canvas to image with javascript

I have a canvas on which I draw pretty images!
Using .toDataURL('image/gif'), I save those images as image files. So far so good.
Thing is that I want to save a number of those images (I change parameters for each image to be saved.)
I would like to do it "silently", that is without having to click on "Save" on the modal window that pops up every time.
Is there a way to do that?
Thanks.
Using the download attribute of an <a> element, you can silently download the image data without needing to deal with any dialog windows. I should warn that this could be potentially dangerous as it is literally downloading a file to your computer without any direct confirmation.
Once you have your data URL from your canvas, the rest is pretty easy. You can create a new <a> element, set the href to your data URL, set the download attribute to the filename you wish to use, and then execute the click() event on this newly created element.
let dataURL
const _Base64Image = url => {
const img = new Image()
img.setAttribute('crossOrigin', 'anonymous')
img.onload = () => {
const canvas = document.createElement("canvas")
canvas.width = img.width
canvas.height = img.height
const ctx = canvas.getContext("2d")
ctx.drawImage(img, 0, 0)
dataURL = canvas.toDataURL("image/png")
_SetBG(document.querySelector("#imgTest"), dataURL, img.width, img.height)
}
img.src = url
}
const _SetBG = (el, data, w, h) => {
el.style.width = `${w}px`
el.style.height = `${h}px`
el.style.backgroundImage = `url("${data}")`
}
const _SaveImage = (data, filename) => {
const a = document.createElement("a")
a.href = data
a.download = filename
a.click()
}
_Base64Image('https://upload.wikimedia.org/wikipedia/commons/thumb/9/99/Unofficial_JavaScript_logo_2.svg/2048px-Unofficial_JavaScript_logo_2.svg.png')
document.querySelector("input[type='button']").addEventListener("click", e => {
_SaveImage(dataURL, "test_image.png")
})
.random-background {
max-height: 100px;
max-width: 100px;
background-size: contain;
}
<div id="imgTest" class="random-background"></div>
<br>
<br>
<input type="button" value="Save Image">
NOTE
This snippet will not work on StackOverflow as part of their security settings. However I also uploaded this to CodePen so you can see a working version.
For the sake of not forcing downloads on anyone I included a button that must be clicked to save the image. However this is not required for the code to work. Simply calling the _SaveImage() function (and passing in the data URL and filename) will work. But having the snippet do this automatically is somewhat problematic as you can get hit with multiple downloads for the same file just by going to the page.

Refresh canvas everytime I want to download a new image

i'm working on a web app where you can upload an image, put a png image over, and download the image with the sticker applied.
For that i'm using html2canvas, and it's working fine, you upload your photo, choose the png and download the new image, so far everyting is fine, but if you want to change the png and download the new image it will dowload the first image again with the first png. Im checking and im creating everytime a new canvas, but evertyme the download button uses the first canvas. How could i refresh that canvas to donwload the last one everytime?
This is the code:
<script>
const capture = () => {
const body = document.querySelector('#ImagenLista');
body && (body.id = 'CapturaPantalla');
html2canvas(document.querySelector("#CapturaPantalla")).then(canvas => {
document.body.appendChild(canvas);
}).then(() => {
var canvas = document.querySelector('canvas');
canvas.style.display = 'none';
var image = canvas.toDataURL("image/jpg").replace("image/jpg", "image/octet-stream");
var a = document.createElement("a");
a.setAttribute('download', 'YoSoyYVoyA.jpg');
a.setAttribute('href', image);
a.click();
});
};
const btn = document.getElementById('DescargaImagen');
function FuncionDescarga() {
btn.addEventListener('click', capture);
}
</script>
Thank you so much.

Execute Function After Canvas toDataURL() Function is Complete

I'm converting images to base64 using canvas. What i need to do is convert those images and then show the result to the user (original image and base64 version). Everything works as expected with small images, but when i try to convert large images (>3MB) and the conversion time increases, the base64 version is empty.
This might be is caused because the result is shown before the toDataURL() function is completed.
I need to show the result after all the needed processing has ended, for testing purposes.
Here's my code:
var convertToBase64 = function(url, callback)
{
var image = new Image();
image.onload = function ()
{
//create canvas and draw image...
var imageData = canvas.toDataURL('image/png');
callback(imageData);
};
image.src = url;
};
convertToBase64('img/circle.png', function(imageData)
{
window.open(imageData);
});
Even though i'm using image.onload() with a callback, i'm unable to show the result after the toDataURL() has been processed.
What am i doing wrong?
UPDATE: I tried both the solutions below and they didn't work. I'm using AngularJS and Electron in this project. Any way i can force the code to be synchronous? Or maybe some solution using Promises?
UPDATE #2: #Kaiido pointed out that toDataURL() is in fact synchronous and this issue is more likely due to maximum URI length. Since i'm using Electron and the image preview was for testing purposes only, i'm going to save the file in a folder and analise it from there.
Your code seems absolutely fine. Not sure why isn't working you. Maybe, there are some issues with your browser. Perhaps try using a different one. Also you could use a custom event, which gets triggered when the image conversion is competed.
// using jQuery for custom event
function convertToBase64(url) {
var image = new Image();
image.src = url;
image.onload = function() {
var canvas = document.createElement('canvas');
canvas.width = image.width;
canvas.height = image.height;
var ctx = canvas.getContext('2d');
ctx.drawImage(image, 0, 0);
var imageData = canvas.toDataURL();
$(document).trigger('conversionCompleted', imageData);
};
};
convertToBase64('4mb.jpg');
$(document).on('conversionCompleted', function(e, d) {
window.open(d);
});
This approach might work for you. It shows the image onscreen using the native html element, then draws it to a canvas, then converts the canvas to Base64, then clears the canvas and draws the converted image onto the canvas. You can then scroll between the top image (original) and the bottom image (converted). I tried it on large images and it takes a second or two for the second image to draw but it seems to work...
Html is here:
<img id="imageID">
<canvas id="myCanvas" style="width:400;height:400;">
</canvas>
Script is here:
var ctx;
function convertToBase64(url, callback)
{
var image = document.getElementById("imageID");
image.onload = function() {
var canvas = document.getElementById("myCanvas");
canvas.width = image.naturalWidth;
canvas.height = image.naturalHeight;
ctx = canvas.getContext("2d");
ctx.drawImage(image,0,0);
var imageData = canvas.toDataURL('image/png');
ctx.fillStyle ="#FFFFFF";
ctx.fillRect(0,0,canvas.width,canvas.height);
callback(imageData);
};
image.src = url;
};
var imagename = 'images/bigfiletest.jpg';
window.onload = function () {
convertToBase64(imagename, function(imageData) {
var myImage = new Image();
myImage.src = imageData;
ctx.drawImage(myImage,0,0);
});
}
Note that I also tried it without the callback and it worked fine as well...

Input onchange javascript function call not working in Safari

I have an html canvas that shows a preview of an image when the image is selected in an input. This works in Chrome, but I can't seem to get it to work in Safari. Specifically - in Safari - the onchange="previewFile()" does not seem to call the previewFile function.
<canvas id="canvas" width="0" height="0"></canvas>
<h2>Upload a photo </h2>
<input type="file" onchange="previewFile()"><br>
<script type="text/javascript">
// setup the canvas
var canvas = document.getElementById('canvas');
var ctx = canvas.getContext('2d');
// grab the photo and display in canvas
var photo = new Image();
function previewFile() {
var file    = document.querySelector('input[type=file]').files[0];
var reader  = new FileReader(); 
reader.addEventListener("load", function () {
photo.src = reader.result;
canvas.height = photo.height;
canvas.width = photo.width;
ctx.drawImage(photo,0,0);
}, false);
if (file) {
  reader.readAsDataURL(file);
}
}
</script>
Your problem is certainly due to the fact that you are not waiting for the image has loaded before you try to draw it on the canvas.
Even wen the source is a dataURI, the loading of the image is asynchronous, so you need to wrap your drawing operations in the image's onload event.
var photo = new Image();
photo.onload = function(){
canvas.height = photo.height;
...
}
...
reader.onload = function(){
// this will eventually trigger the image's onload event
photo.src = this.result;
}
But if all you need is to draw your image on the canvas, don't even use a FileReader, actually, the readAsDataURL() method should trigger a I'm doing something wrong error in your brain. Almost all you can do with a dataURI version of a Blob, you can also do it with the Blob itself, without computing it nor polluting the browser's memory.
For example, to display an image for user input, you can use URL.createObjectURL(blob) method.
// setup the canvas
var canvas = document.getElementById('canvas');
var ctx = canvas.getContext('2d');
// grab the photo and display in canvas
var photo = new Image();
// drawing operations should be in the mage's load event
photo.onload = function() {
// if you don't need to display this image elsewhere
URL.revokeObjectURL(this.src);
canvas.height = this.height;
canvas.width = this.width;
ctx.drawImage(this, 0, 0);
}
photo.onerror = function(e) {
console.warn('file format not recognised as a supported image');
}
file_input.onchange = function() {
// prefer 'this' over DOM selection
var file = this.files[0];
var url = URL.createObjectURL(file);
photo.src = url;
};
<canvas id="canvas" width="0" height="0"></canvas>
<h2>Upload a photo </h2>
<input type="file" id="file_input">
<br>
And for those who need to send this file to their server, use a FormData to send directly the Blob. If you really need a dataURI version, then convert it server-side.

Display Image using canvas in JavaScript/jQuery

I have the following code :
function createImage(source) {
var pastedImage = new Image();
pastedImage.onload = function() {
document.write('<br><br><br>Image: <img src="'+pastedImage.src+'" height="700" width="700"/>');
}
pastedImage.src = source;
}
Here I am displaying the image through html image tag which I wrote in document.write and provide appropriate height and width to image.
My question is can it possible to displaying image into the canvas instead of html img tag? So that I can drag and crop that image as I want?
But how can I display it in canvas?
Further I want to implement save that image using PHP but for now let me know about previous issue.
Try This
var ctx = document.getElementById('canvas').getContext('2d');
var img = new Image(); // Create new img element
img.onload = function(){
// execute drawImage statements here This is essential as it waits till image is loaded before drawing it.
ctx.drawImage(img , 0, 0);
};
img.src = 'myImage.png'; // Set source path
Make sure the image is hosted in same domain as your site. Read this for Javascript Security Restrictions Same Origin Policy.
E.g. If your site is http://example.com/
then the Image should be hosted on http://example.com/../myImage.png
if you try http://facebook.com/..image/ or something then it will throw security error.
Use
CanvasRenderingContext2D.drawImage.
function createImage(source) {
var ctx = document.getElementById('canvas').getContext('2d');
var pastedImage = new Image();
pastedImage.onload = function(){
ctx.drawImage(pastedImage, 0, 0);
};
pastedImage = source;
}
Also MDN seems to be have nice examples.

Categories