I've been trying to get a Base64 string from a canvas. But I cannot.
The following code does nothing. It should be insert an image into document.body. On the other hand I've tried to get base64 string and converting an image. But again I cannot.
what should I do?
http://jsfiddle.net/27hdp4y6/1/
var image1 = new Image();
image1.src = "http://www.w3.org/html/logo/downloads/HTML5_Logo_128.png";
var image2 = new Image();
image2.src = "http://prismitsolutions.com/images/icons/css3.png";
var canvas = document.createElement('canvas');
canvas.width = 220;
canvas.height= image1.height;
var context = canvas.getContext("2d");
image1.onload = function() {
context.drawImage(image1, 0, 0);
context.drawImage(image2, 80, 0);
};
var base64 = canvas.toDataURL('image/png');
var img = document.createElement('img');
img.src=base64;
document.body.appendChild(img);
//document.body.appendChild(canvas);
You are getting an empty canvas as your code does not wait for the images to load properly. Image loading is asynchronous.
You are also assuming that both images has loaded when image 1 has finished.
You need to wait for both images to load
Call toDataURL() from inside the load handler (or call another function to do so) after drawing the images.
You are trying to set the height of canvas from an image that is likely not loaded yet. Set it when you have the images loaded
You may get CORS restriction triggered here, make sure the images exist on the same server as page or that the server allow cross-origin usage. If not this will throw a security error.
var image1 = new Image();
var image2 = new Image();
var count = 2; // two images to load, for more use an array instead
image1.onload = image2.onload = function() {
count--;
if (!count) {
var canvas = document.createElement('canvas');
canvas.width = 220;
canvas.height = image1.height;
var context = canvas.getContext("2d");
context.drawImage(image1, 0, 0);
context.drawImage(image2, 80, 0);
// the data-uri, use f.ex. a callback function with this as
// argument to process it further (not shown here)
var base64 = canvas.toDataURL('image/png');
var img = document.createElement('img'); // ideally, use a handler here too
img.src = base64;
document.body.appendChild(img);
}
};
// we need cross-origin in this case/demo as images exists on a different server
// imgur.com allow using cross-origin, not all servers do though...
image1.crossOrigin = image2.crossOrigin = "anonymous";
// set sources after handlers are defined
image2.src = "http://i.imgur.com/bk0rvnj.png";
image1.src = "http://i.imgur.com/wlPnCM1.png";
Use appendChild() cf.http://jsfiddle.net/27hdp4y6/1/
Just add :
...
document.body.appendChild(canvas);
var context = canvas.getContext("2d");
...
First of all JS is async! So your images will load async too.
You should append canvas to DOM:
document.body.appendChild(canvas);
Work example: http://jsfiddle.net/27hdp4y6/2/
Related
I am trying to convert an img URL to a var that I can use elsewhere in my code.
So far, I have the following approach:
function toDataUrl(src) {
// Create an Image object
var img = new Image();
// Add CORS approval to prevent a tainted canvas
img.crossOrigin = 'Anonymous';
// Load the image
img.src = src;
// make sure the load event fires for cached images too
if (img.complete || img.complete === undefined) {
// Flush cache
img.src = 'data:image/gif;base64,R0lGODlhAQABAIAAAAAAAP///ywAAAAAAQABAAACAUwAOw==';
// Try again
img.src = src;
}
img.onload = function() {
// Create an html canvas element
var canvas = document.createElement('CANVAS');
// Create a 2d context
var ctx = canvas.getContext('2d');
var dataURL;
// Resize the canavas to the original image dimensions
canvas.height = this.naturalHeight;
canvas.width = this.naturalWidth;
// Draw the image to a canvas
ctx.drawImage(this, 0, 0);
// Convert the canvas to a data url
dataURL = canvas.toDataURL(outputFormat);
// Mark the canvas to be ready for garbage
// collection
canvas = null;
// Return the data url via callback
// callback(dataURL);
return dataURL;
};
}
var myimg = toDataUrl('/media/signatures/signature.8.png');
However, myimg returns undefined. I think this is because it might be loading asynchronously. I have refered to this answer however it does not explain how to set the callback as a global variable.
How can I set myimg = dataURL? Is there a way to do this without a callback?
There's no way to break the entire execution model of JavaScript in order to get your asynchronously-populated variable returned from your function call.
Although this uses callbacks behind the scenes, you may find using an async function pattern to relieve the burden of needing to use callbacks.
function toDataUrl(src) {
// Create an Image object
var img = new Image();
// Add CORS approval to prevent a tainted canvas
img.crossOrigin = 'Anonymous';
// Load the image
img.src = src;
// make sure the load event fires for cached images too
if (img.complete || img.complete === undefined) {
// Flush cache
img.src = 'data:image/gif;base64,R0lGODlhAQABAIAAAAAAAP///ywAAAAAAQABAAACAUwAOw==';
// Try again
img.src = src;
}
return new Promise((resolve, reject) => {
img.addEventListener('load', () => {
// Create an html canvas element
var canvas = document.createElement('CANVAS');
// Create a 2d context
var ctx = canvas.getContext('2d');
var dataURL;
// Resize the canavas to the original image dimensions
canvas.height = this.naturalHeight;
canvas.width = this.naturalWidth;
// Draw the image to a canvas
ctx.drawImage(this, 0, 0);
// Convert the canvas to a data url
dataURL = canvas.toDataURL(outputFormat);
// Mark the canvas to be ready for garbage
// collection
canvas = null;
resolve(dataURL);
});
img.addEventListener('error', reject);
});
}
This now can be consumed within an async function thusly:
async function doSomething() {
const myimg = await toDataUrl('/media/signatures/signature.8.png');
}
Keep in mind that doSomething is now also asynchronous, so anything calling it now has to await as well (or use Promise callbacks).
I need to load a png file from my computer and loop through each pixel value. I tried canvas, but it breaks with the cross-domain stuff I dont understand...
var img = new Image();
img.src = "map.png";
var canvas = document.getElementById("secondaryCanvas");
var ctx = canvas.getContext("2d");
ctx.drawImage(img, 0, 0, 30, 30);
console.log(ctx.getImageData(0,0,1,1));
...Sometimes it works, sometimes it doesnt?... Im absolutely lost
You can not read the pixel data if the page is being load directly from the file system. You must load the page from a web server. Install apache and load it from localhost
About the fact that it sometimes "work" it depends on whether the image has been draw in the canvas when you call getImageData.
if the image has not been loaded you can call getImageData because you are not reading the image data but the blank canvas.
if you call the getImageData inside the image onload event the code will always fail
var img = new Image();
img.onload = function() {
console.log(this)
var canvas = document.getElementById("secondaryCanvas");
var ctx = canvas.getContext("2d");
ctx.drawImage(img, 0, 0);
console.log(ctx.getImageData(100,100,1,1));
}
img.src = "map.png";
This is a quick (althopugh not clean) example that I made on the bus just because I couldn't believe that nobody answered with this hack
If you want to test it save it as index.html and open it with chrome
TLDR; load file as base64 then use it as src for the image
<script>
async function toBase64(file) {
return new Promise((resolve, reject) => {
const reader = new FileReader();
reader.readAsDataURL(file);
reader.onload = () => resolve(reader.result);
reader.onerror = error => reject(error);
});
}
async function reset() {
const input = document.querySelector('input');
console.log(input.files)
const base64 = await toBase64(input.files[0]);
console.log(base64)
const src = base64;
var img = new Image();
img.onload = function () {
console.log(this)
var canvas = document.getElementById("secondaryCanvas");
var ctx = canvas.getContext("2d");
ctx.drawImage(img, 0, 0);
console.log(ctx.getImageData(100, 100, 1, 1));
}
img.src = src;
}
</script>
<input type="file" id="input" onchange="reset()" />
<canvas id="secondaryCanvas"></canvas>
I have a regular HTML page with some images (just regular <img /> HTML tags). I'd like to get their content, base64 encoded preferably, without the need to redownload the image (ie. it's already loaded by the browser, so now I want the content).
I'd love to achieve that with Greasemonkey and Firefox.
Note: This only works if the image is from the same domain as the page, or has the crossOrigin="anonymous" attribute and the server supports CORS. It's also not going to give you the original file, but a re-encoded version. If you need the result to be identical to the original, see Kaiido's answer.
You will need to create a canvas element with the correct dimensions and copy the image data with the drawImage function. Then you can use the toDataURL function to get a data: url that has the base-64 encoded image. Note that the image must be fully loaded, or you'll just get back an empty (black, transparent) image.
It would be something like this. I've never written a Greasemonkey script, so you might need to adjust the code to run in that environment.
function getBase64Image(img) {
// Create an empty canvas element
var canvas = document.createElement("canvas");
canvas.width = img.width;
canvas.height = img.height;
// Copy the image contents to the canvas
var ctx = canvas.getContext("2d");
ctx.drawImage(img, 0, 0);
// Get the data-URL formatted image
// Firefox supports PNG and JPEG. You could check img.src to
// guess the original format, but be aware the using "image/jpg"
// will re-encode the image.
var dataURL = canvas.toDataURL("image/png");
return dataURL.replace(/^data:image\/(png|jpg);base64,/, "");
}
Getting a JPEG-formatted image doesn't work on older versions (around 3.5) of Firefox, so if you want to support that, you'll need to check the compatibility. If the encoding is not supported, it will default to "image/png".
This Function takes the URL then returns the image BASE64
function getBase64FromImageUrl(url) {
var img = new Image();
img.setAttribute('crossOrigin', 'anonymous');
img.onload = function () {
var canvas = document.createElement("canvas");
canvas.width =this.width;
canvas.height =this.height;
var ctx = canvas.getContext("2d");
ctx.drawImage(this, 0, 0);
var dataURL = canvas.toDataURL("image/png");
alert(dataURL.replace(/^data:image\/(png|jpg);base64,/, ""));
};
img.src = url;
}
Call it like this :
getBase64FromImageUrl("images/slbltxt.png")
Coming long after, but none of the answers here are entirely correct.
When drawn on a canvas, the passed image is uncompressed + all pre-multiplied.
When exported, its uncompressed or recompressed with a different algorithm, and un-multiplied.
All browsers and devices will have different rounding errors happening in this process
(see Canvas fingerprinting).
So if one wants a base64 version of an image file, they have to request it again (most of the time it will come from cache) but this time as a Blob.
Then you can use a FileReader to read it either as an ArrayBuffer, or as a dataURL.
function toDataURL(url, callback){
var xhr = new XMLHttpRequest();
xhr.open('get', url);
xhr.responseType = 'blob';
xhr.onload = function(){
var fr = new FileReader();
fr.onload = function(){
callback(this.result);
};
fr.readAsDataURL(xhr.response); // async call
};
xhr.send();
}
toDataURL(myImage.src, function(dataURL){
result.src = dataURL;
// now just to show that passing to a canvas doesn't hold the same results
var canvas = document.createElement('canvas');
canvas.width = myImage.naturalWidth;
canvas.height = myImage.naturalHeight;
canvas.getContext('2d').drawImage(myImage, 0,0);
console.log(canvas.toDataURL() === dataURL); // false - not same data
});
<img id="myImage" src="https://dl.dropboxusercontent.com/s/4e90e48s5vtmfbd/aaa.png" crossOrigin="anonymous">
<img id="result">
A more modern version of kaiido's answer using fetch would be:
function toObjectUrl(url) {
return fetch(url)
.then((response)=> {
return response.blob();
})
.then(blob=> {
return URL.createObjectURL(blob);
});
}
https://developer.mozilla.org/en-US/docs/Web/API/Fetch_API/Using_Fetch
Edit: As pointed out in the comments this will return an object url which points to a file in your local system instead of an actual DataURL so depending on your use case this might not be what you need.
You can look at the following answer to use fetch and an actual dataURL: https://stackoverflow.com/a/50463054/599602
shiv / shim / sham
If your image(s) are already loaded (or not), this "tool" may come in handy:
Object.defineProperty
(
HTMLImageElement.prototype,'toDataURL',
{enumerable:false,configurable:false,writable:false,value:function(m,q)
{
let c=document.createElement('canvas');
c.width=this.naturalWidth; c.height=this.naturalHeight;
c.getContext('2d').drawImage(this,0,0); return c.toDataURL(m,q);
}}
);
.. but why?
This has the advantage of using the "already loaded" image data, so no extra request is needed. Additionally it lets the end-user (programmer like you) decide the CORS and/or mime-type and quality -OR- you can leave out these arguments/parameters as described in the MDN specification here.
If you have this JS loaded (prior to when it's needed), then converting to dataURL is as simple as:
examples
HTML
<img src="/yo.jpg" onload="console.log(this.toDataURL('image/jpeg'))">
JS
console.log(document.getElementById("someImgID").toDataURL());
GPU fingerprinting
If you are concerned about the "preciseness" of the bits then you can alter this tool to suit your needs as provided by #Kaiido's answer.
its 2022, I prefer to use modern createImageBitmap() instead of onload event.
*note: image should be same origin or CORS enabled
async function imageToDataURL(imageUrl) {
let img = await fetch(imageUrl);
img = await img.blob();
let bitmap = await createImageBitmap(img);
let canvas = document.createElement("canvas");
let ctx = canvas.getContext("2d");
canvas.width = bitmap.width;
canvas.height = bitmap.height;
ctx.drawImage(bitmap, 0, 0, bitmap.width, bitmap.height);
return canvas.toDataURL("image/png");
// image compression?
// return canvas.toDataURL("image/png", 0.9);
};
(async() => {
let dataUrl = await imageToDataURL('https://en.wikipedia.org/static/images/project-logos/enwiki.png')
wikiImg.src = dataUrl;
console.log(dataUrl)
})();
<img id="wikiImg">
Use onload event to convert image after loading
function loaded(img) {
let c = document.createElement('canvas')
c.getContext('2d').drawImage(img, 0, 0)
msg.innerText= c.toDataURL();
}
pre { word-wrap: break-word; width: 500px; white-space: pre-wrap; }
<img onload="loaded(this)" src="https://cors-anywhere.herokuapp.com/http://lorempixel.com/200/140" crossorigin="anonymous"/>
<pre id="msg"></pre>
This is all you need to read.
https://developer.mozilla.org/en-US/docs/Web/API/FileReader/readAsBinaryString
var height = 200;
var width = 200;
canvas.width = width;
canvas.height = height;
var ctx = canvas.getContext('2d');
ctx.strokeStyle = '#090';
ctx.beginPath();
ctx.arc(width/2, height/2, width/2 - width/10, 0, Math.PI*2);
ctx.stroke();
canvas.toBlob(function (blob) {
//consider blob is your file object
var reader = new FileReader();
reader.onload = function () {
console.log(reader.result);
}
reader.readAsBinaryString(blob);
});
In HTML5 better use this:
{
//...
canvas.width = img.naturalWidth; //img.width;
canvas.height = img.naturalHeight; //img.height;
//...
}
I have a regular HTML page with some images (just regular <img /> HTML tags). I'd like to get their content, base64 encoded preferably, without the need to redownload the image (ie. it's already loaded by the browser, so now I want the content).
I'd love to achieve that with Greasemonkey and Firefox.
Note: This only works if the image is from the same domain as the page, or has the crossOrigin="anonymous" attribute and the server supports CORS. It's also not going to give you the original file, but a re-encoded version. If you need the result to be identical to the original, see Kaiido's answer.
You will need to create a canvas element with the correct dimensions and copy the image data with the drawImage function. Then you can use the toDataURL function to get a data: url that has the base-64 encoded image. Note that the image must be fully loaded, or you'll just get back an empty (black, transparent) image.
It would be something like this. I've never written a Greasemonkey script, so you might need to adjust the code to run in that environment.
function getBase64Image(img) {
// Create an empty canvas element
var canvas = document.createElement("canvas");
canvas.width = img.width;
canvas.height = img.height;
// Copy the image contents to the canvas
var ctx = canvas.getContext("2d");
ctx.drawImage(img, 0, 0);
// Get the data-URL formatted image
// Firefox supports PNG and JPEG. You could check img.src to
// guess the original format, but be aware the using "image/jpg"
// will re-encode the image.
var dataURL = canvas.toDataURL("image/png");
return dataURL.replace(/^data:image\/(png|jpg);base64,/, "");
}
Getting a JPEG-formatted image doesn't work on older versions (around 3.5) of Firefox, so if you want to support that, you'll need to check the compatibility. If the encoding is not supported, it will default to "image/png".
This Function takes the URL then returns the image BASE64
function getBase64FromImageUrl(url) {
var img = new Image();
img.setAttribute('crossOrigin', 'anonymous');
img.onload = function () {
var canvas = document.createElement("canvas");
canvas.width =this.width;
canvas.height =this.height;
var ctx = canvas.getContext("2d");
ctx.drawImage(this, 0, 0);
var dataURL = canvas.toDataURL("image/png");
alert(dataURL.replace(/^data:image\/(png|jpg);base64,/, ""));
};
img.src = url;
}
Call it like this :
getBase64FromImageUrl("images/slbltxt.png")
Coming long after, but none of the answers here are entirely correct.
When drawn on a canvas, the passed image is uncompressed + all pre-multiplied.
When exported, its uncompressed or recompressed with a different algorithm, and un-multiplied.
All browsers and devices will have different rounding errors happening in this process
(see Canvas fingerprinting).
So if one wants a base64 version of an image file, they have to request it again (most of the time it will come from cache) but this time as a Blob.
Then you can use a FileReader to read it either as an ArrayBuffer, or as a dataURL.
function toDataURL(url, callback){
var xhr = new XMLHttpRequest();
xhr.open('get', url);
xhr.responseType = 'blob';
xhr.onload = function(){
var fr = new FileReader();
fr.onload = function(){
callback(this.result);
};
fr.readAsDataURL(xhr.response); // async call
};
xhr.send();
}
toDataURL(myImage.src, function(dataURL){
result.src = dataURL;
// now just to show that passing to a canvas doesn't hold the same results
var canvas = document.createElement('canvas');
canvas.width = myImage.naturalWidth;
canvas.height = myImage.naturalHeight;
canvas.getContext('2d').drawImage(myImage, 0,0);
console.log(canvas.toDataURL() === dataURL); // false - not same data
});
<img id="myImage" src="https://dl.dropboxusercontent.com/s/4e90e48s5vtmfbd/aaa.png" crossOrigin="anonymous">
<img id="result">
A more modern version of kaiido's answer using fetch would be:
function toObjectUrl(url) {
return fetch(url)
.then((response)=> {
return response.blob();
})
.then(blob=> {
return URL.createObjectURL(blob);
});
}
https://developer.mozilla.org/en-US/docs/Web/API/Fetch_API/Using_Fetch
Edit: As pointed out in the comments this will return an object url which points to a file in your local system instead of an actual DataURL so depending on your use case this might not be what you need.
You can look at the following answer to use fetch and an actual dataURL: https://stackoverflow.com/a/50463054/599602
shiv / shim / sham
If your image(s) are already loaded (or not), this "tool" may come in handy:
Object.defineProperty
(
HTMLImageElement.prototype,'toDataURL',
{enumerable:false,configurable:false,writable:false,value:function(m,q)
{
let c=document.createElement('canvas');
c.width=this.naturalWidth; c.height=this.naturalHeight;
c.getContext('2d').drawImage(this,0,0); return c.toDataURL(m,q);
}}
);
.. but why?
This has the advantage of using the "already loaded" image data, so no extra request is needed. Additionally it lets the end-user (programmer like you) decide the CORS and/or mime-type and quality -OR- you can leave out these arguments/parameters as described in the MDN specification here.
If you have this JS loaded (prior to when it's needed), then converting to dataURL is as simple as:
examples
HTML
<img src="/yo.jpg" onload="console.log(this.toDataURL('image/jpeg'))">
JS
console.log(document.getElementById("someImgID").toDataURL());
GPU fingerprinting
If you are concerned about the "preciseness" of the bits then you can alter this tool to suit your needs as provided by #Kaiido's answer.
its 2022, I prefer to use modern createImageBitmap() instead of onload event.
*note: image should be same origin or CORS enabled
async function imageToDataURL(imageUrl) {
let img = await fetch(imageUrl);
img = await img.blob();
let bitmap = await createImageBitmap(img);
let canvas = document.createElement("canvas");
let ctx = canvas.getContext("2d");
canvas.width = bitmap.width;
canvas.height = bitmap.height;
ctx.drawImage(bitmap, 0, 0, bitmap.width, bitmap.height);
return canvas.toDataURL("image/png");
// image compression?
// return canvas.toDataURL("image/png", 0.9);
};
(async() => {
let dataUrl = await imageToDataURL('https://en.wikipedia.org/static/images/project-logos/enwiki.png')
wikiImg.src = dataUrl;
console.log(dataUrl)
})();
<img id="wikiImg">
Use onload event to convert image after loading
function loaded(img) {
let c = document.createElement('canvas')
c.getContext('2d').drawImage(img, 0, 0)
msg.innerText= c.toDataURL();
}
pre { word-wrap: break-word; width: 500px; white-space: pre-wrap; }
<img onload="loaded(this)" src="https://cors-anywhere.herokuapp.com/http://lorempixel.com/200/140" crossorigin="anonymous"/>
<pre id="msg"></pre>
This is all you need to read.
https://developer.mozilla.org/en-US/docs/Web/API/FileReader/readAsBinaryString
var height = 200;
var width = 200;
canvas.width = width;
canvas.height = height;
var ctx = canvas.getContext('2d');
ctx.strokeStyle = '#090';
ctx.beginPath();
ctx.arc(width/2, height/2, width/2 - width/10, 0, Math.PI*2);
ctx.stroke();
canvas.toBlob(function (blob) {
//consider blob is your file object
var reader = new FileReader();
reader.onload = function () {
console.log(reader.result);
}
reader.readAsBinaryString(blob);
});
In HTML5 better use this:
{
//...
canvas.width = img.naturalWidth; //img.width;
canvas.height = img.naturalHeight; //img.height;
//...
}
var imageUrl = {};
function convertToDataURLviaCanvas(url, item, outputFormat){
var img = new Image();
img.onload = function(){
var canvas = document.createElement('CANVAS');
var ctx = canvas.getContext('2d');
var dataURL;
canvas.height = this.height;
canvas.width = this.width;
ctx.drawImage(this, 0, 0);
dataURL = canvas.toDataURL();
storemapImage(item, dataURL);
console.log(imageUrl);
};
}
function storemapImage(propertyName, url){
imageUrl[propertyName] = {
objectImage: url
};
return;
}
var src = "http://orig11.deviantart.net/c565/f/2013/092/1/6/mario_pixel_by_juli95-d607odz.png"
function capture (){
convertToDataURLviaCanvas(src, "display1", "Image/png");
}
my intention is to turn a normal png image into json data and then store to a object (imageUrl) , here is my code , i tested alot but still didt get anything from it, can anyone take a look for me ?
Demo
I don't think you are going to be happy with the answer here.
First of all, your "demo" link and the code here don't match.
In your code above, you are console logging imageUrl before you actually set it, so it won't be correct. if you move your console.log into the end of your storemapImage you should get a value.
In your demo, you are trying to console.log right after calling convertToDataURLviaCanvas but again it won't be set as onload takes at least a couple milliseconds to actually download the image.
You are also not actually setting img.src so it never attempts to load, and never fires the onload.
So fixing all that, it comes down to cross origin requests. Your browser has a security setting that prevents javascript from accessing things from other domains. Javascript can make an Image object and tell it to load, and you can save it to a canvas, but once you have done that, the canvas is considered tainted and you can not convert it to a url.
https://developer.mozilla.org/en-US/docs/Web/HTML/CORS_enabled_image has more info.
If the remote server has enabled CORS headers, then it can be done, but deviant art has not done so.
var imageUrl = {};
function convertToDataURLviaCanvas(url, item, outputFormat){
var img = new Image();
img.crossOrigin = "Anonymous";
img.src = url;
img.onload = function(){
var canvas = document.createElement('CANVAS');
var ctx = canvas.getContext('2d');
var dataURL;
canvas.height = this.height;
canvas.width = this.width;
ctx.drawImage(this, 0, 0);
dataURL = canvas.toDataURL();
storemapImage(item, url);
};
}
function storemapImage(propertyName, url){
imageUrl[propertyName] = {
objectImage: url
};
console.log('imageUrl', imageUrl)
return;
}
function capture (){
//convertToDataURLviaCanvas("https://orig11.deviantart.net/c565/f/2013/092/1/6/mario_pixel_by_juli95-d607odz.png", "display1", "Image/png");
convertToDataURLviaCanvas("https://s3-us-west-2.amazonaws.com/s.cdpn.io/3/pie.png", "display1", "Image/png");
}
https://jsfiddle.net/svr4r681/ is an updated fiddle with an image that does work, but still doesn't work with deviantart.