How to save and load HTML5 Canvas to & from localStorage? - javascript

I want the user to be able to save canvas drawings to the localStorage (using the Save button)under a custom name and then be able to load (using the load button) previous drawings from the localStorage.

First option
If you want to save just a bitmap then you can save it this way:
localStorage.setItem(canvasName, canvas.toDataURL());
and then load like this:
var dataURL = localStorage.getItem(canvasName);
var img = new Image;
img.src = dataURL;
img.onload = function () {
ctx.drawImage(img, 0, 0);
};
I recommend to use canvas.toDataURL() instead of ctx.getImageData() because ctx.getImageData() JSON string size will be just enormous even if canvas is empty.
Second option
If you want to store canvas as lines array then you should store lines coords in some variable and save it`s json:
localStorage.setItem(canvasName, JSON.stringify(linesArray));
Then you can load lines array and redraw canvas:
var lines = JSON.parse(localStorage.getItem(canvasName));
lines.forEach(function (line) {
ctx.beginPath();
ctx.strokeStyle = line.color;
ctx.moveTo(line.x1, line.y1);
ctx.lineTo(line.x2, line.y2);
ctx.stroke();
});

First step will be to get image data from canvas.
var imageData = myCtxt.getImageData(0,0,WIDTH,HEIGHT);
Now store it to localStorage after stringifying it:
localStorage.setItem(savekey, JSON.stringify(idt));
To read and set the data back you can use following:
var idt = localStorage.getItem(savekey) || null;
if (idt !== null) {
var data = JSON.parse(idt);
ctx.putImageData(idt, 0, 0);
}
I have put the functions to handle the functionality in your fiddle.
Regarding localStorage you can read this and this questions.

Related

Cropping data:image/png;base64 for large print screens

I'm trying to crop large print screens added in contenteditable by ctrl+v. Have tried several ways but I can't get it to work. The last methode I tried is the drawImage that's why I left it inside the code. Think i'm on the wright direction with it? Or do I need a totally other technique?
I want to use the data:image/png;base64 to store it inside the database. But with very large print screens it's to large to store in the database. Any suggestions? Thanks in advance!
Edit: I just noticed that I need to use blob field in database instead of text. This fixed the issue to store larger screenshots in database. But still do people have any advice for the use of blob and store it in database? I think cropping large screenshots would still be an improvement?
document.onpaste = function(pasteEvent) {
var item = pasteEvent.clipboardData.items[0];
if (item.type.indexOf("image") === 0) {
//pasteEvent.preventDefault();
//alert('You can\'t copy & paste images or screenshots yet.');
var blob = item.getAsFile();
var reader = new FileReader();
var size = pasteEvent.clipboardData.files[0].size;
alert(size);
//var canvas = reader;
//var context = canvas.getContext('2d');
//canvas.width = canvas.height = 64;
//context.drawImage(img, 64,64, 64, 64, 0, 0, 64, 64);
//mod.src = canvas.toDataURL();
reader.onload = function(event) {
document.getElementById("container").src = event.target.result;
};
reader.readAsDataURL(blob);
}
if (item.type.indexOf("text") === 0) {
pasteEvent.preventDefault();
const text = pasteEvent.clipboardData.getData('text');
document.execCommand("insertHTML", false, text);
}
}
I guess these lines of code will work
var canvas = reader;
var context = canvas.getContext('2d');
context.drawImage(img, 0,0, canvas.width, canvas.height);
mod.src = canvas.toDataURL();
Thanks

Saving current work from multiple canvas elements

Here is my problem: I have created an image collage function in javascript. (I started off with some code from this post btw: dragging and resizing an image on html5 canvas)
I have 10 canvas elements stacked on top of each other and all parameters, including 2dcontext, image data, positions etc. for each canvas is held in instances of the function 'collage'.
This is working fine, I can manipulate each canvas separately (drag, resize, adding frames, etc). But now and I want the user to be able to save the current work.
So I figure that maybe it would be possible to create a blob, that contains all the object instances, and then save the blob as a file on disk.
This is the function collage (I also push each instance to the array collage.instances, to be able to have numbered indexes)
function collage() {
this.canvas_board = '';
this.canvas = '';
this.ctx = '';
this.canvasOffset = '';
this.offsetX = '';
this.offsetY = '';
this.startX = '';
this.startY = '';
this.imageX = '';
this.imageY = '';
this.mouseX = '';
this.mouseY = '';
this.imageWidth = '';
this.imageHeight = '';
this.imageRight = '';
this.imageBottom = '';
this.imgframe = '';
this.frame = 'noframe';
this.img = '';
collage.instances.push(this);
}
collage.instances = [];
I tried with something like this:
var oMyBlob = new Blob(collage.instances, {type: 'multipart/form-data'});
But that doesn't work (only contains about 300 bits of data).
Anyone who can help? Or maybe suggest an alternative way to save the current collage work. It must of course must be possible to open the blob and repopulate the object instances.
Or maybe I am making this a bit more complicated than it has to be... but I am stuck right now, so I would appreciate any hints.
You can extract each layer's image data to DataURLs and save the result as a json object.
Here's a quick demo: http://codepen.io/gunderson/pen/PqWZwW
The process literally takes each canvas and saves out its data for later import.
The use of jquery here is for convenience:
$(".save-button").click(function() {
var imgData = JSON.stringify({
layers: getLayerData()
});
save(imgData, "myfile.json");
});
function save(filecontents, filename) {
try {
var $a = $("<a>").attr({
href: "data:application/json;," + filecontents,
download: filename
})[0].click();
return filecontents;
} catch (err) {
console.error(err);
return null;
}
}
function getLayerData() {
var imgData = [];
$(".layer").each(function(i, el) {
imgData.push(el.toDataURL("image/png"));
});
return imgData;
}
To restore, you can use a FileReader to read the contents of the JSON back into the browser, then make <img>s for each layer, set img.src to the dataURLs in your JSON and from there you can draw the <img> into onload canvases.
Add a reference (src URL) for the image to the instance, then serialize the instance array as JSON and use f.ex. localStorage.
localStorage.setItem("currentwork", JSON.stringify(collage.instances));
Then to restore you would need to do:
var tmp = localStorage.getItem("currentwork");
collage.instances = tmp ? JSON.parse(tmp) : [];
You then need to iterate through the array and reload the images using proper onload handling. Finally re-render everything.
Can you store image data on client? Yes, but not recommended. This will take a lot of space and if too much you will not be able to save all the data, the user may refuse to allow more storage space etc.
Keeping a link to the image on a server is a better approach for these things IMO. But if you disagree, look into IndexedDB (or WebSQL although deprecated) to have local storage which can be expanded in available space. localStorage can only hold between 2.5 - 5 mb, ie. no image data and only strings. Each char takes two bytes, data-uris adds 33% on top, so this will run empty pretty fast...

Load file into IMAGE object using Phantom.js

I'm trying to load image and put its data into HTML Image element but without success.
var fs = require("fs");
var content = fs.read('logo.png');
After reading content of the file I have to convert it somehow to Image or just print it to canvas. I was trying to conver binary data to Base64 Data URL with the code I've found on Stack.
function base64encode(binary) {
return btoa(unescape(encodeURIComponent(binary)));
}
var base64Data = 'data:image/png;base64,' +base64encode(content);
console.log(base64Data);
Returned Base64 is not valid Data URL. I was trying few more approaches but without success. Do you know the best (shortest) way to achieve that?
This is a rather ridiculous workaround, but it works. Keep in mind that PhantomJS' (1.x ?) canvas is a bit broken. So the canvas.toDataURL function returns largely inflated encodings. The smallest that I found was ironically image/bmp.
function decodeImage(imagePath, type, callback) {
var page = require('webpage').create();
var htmlFile = imagePath+"_temp.html";
fs.write(htmlFile, '<html><body><img src="'+imagePath+'"></body></html>');
var possibleCallback = type;
type = callback ? type : "image/bmp";
callback = callback || possibleCallback;
page.open(htmlFile, function(){
page.evaluate(function(imagePath, type){
var img = document.querySelector("img");
// the following is copied from http://stackoverflow.com/a/934925
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.
window.dataURL = canvas.toDataURL(type);
}, imagePath, type);
fs.remove(htmlFile);
var dataUrl = page.evaluate(function(){
return window.dataURL;
});
page.close();
callback(dataUrl, type);
});
}
You can call it like this:
decodeImage('logo.png', 'image/png', function(imgB64Data, type){
//console.log(imgB64Data);
console.log(imgB64Data.length);
phantom.exit();
});
or this
decodeImage('logo.png', function(imgB64Data, type){
//console.log(imgB64Data);
console.log(imgB64Data.length);
phantom.exit();
});
I tried several things. I couldn't figure out the encoding of the file as returned by fs.read. I also tried to dynamically load the file into the about:blank DOM through file://-URLs, but that didn't work. I therefore opted to write a local html file to the disk and open it immediately.

How convert downloaded inline image into Base64 on client side

Is there any technique to convert images that have already been downloaded – inline JPEG/GIF/etc. images that occur in a webpage – into Base64 data using client-side JavaScript?
I am not talking about how to transform an image into Base64 using other means (server-side, online tools, etc.).
These are the constraints for my particular use case:
The image is on screen now, right in the page, in front of person. It has already been downloaded in a data sense.
The conversion from raw image data has to be done client-side.
The images in question are from arbitrary domains. That is, they may or may not, be of same origin domain.
The user, if needed (if helpful to solution), can give additional permissions (for example, a FF toolbar install to help skirt cross-domain and other issues). That is, code can be given special endorsement on the client side if that helps solve the issue.
The end goal is to transform all images on the page (in the DOM) into Base64 data inside of JavaScript. Put another way, every image the user can see on the page has been converted into a JavaScript variable of some sort that contains the Base64 data.
So far I see no posts that stay inside of all the above constraints.
I think this is close to what you are looking for but the only problem is that it only works for locally hosted images and HTML5 only.
function toURL(image) {
var canvas = document.createElement("canvas");
canvas.width = image.width;
canvas.height = image.height;
var context = canvas.getContext("2d");
context.drawImage(image, 0, 0);
var s = canvas.toDataURL();
return s.substring(s.indexOf(","));
}
var test = document.getElementById("myImage");
console.log(toURL(test));
You can trick javascript into thinking an image is from your domain with the following code.
image.php
<?php
$image = getAnImagePathAndTypeFromTheDatabaseByID($_GET["id"]);
//returns something like
//array("path" => "http://www.anotherwebsite.com/image.png", "type" => "png")
header("Content-type: image/$image[type]");
echo file_get_contents($image["path"]);
?>
Then just navigate to image.php?id=1 for example.
For it to work in cross-domain client-side you need to call the image with the attribute crossorigin = "true", or, add a line in the Logan Murphy code:
function toURL(image) {
image.setAttribute('crossOrigin', 'anonymous');
var canvas = document.createElement("canvas");
canvas.width = image.width;
canvas.height = image.height;
var context = canvas.getContext("2d");
context.drawImage(image, 0, 0);
var s = canvas.toDataURL();
return s.substring(s.indexOf(","));
}
I use this code:
// image-to-uri.js v1
// converts a URL of an image into a dataURI
function imageToURI(url, callback) {
// Create an empty canvas and image elements
let canvas = document.createElement('canvas');
let img = document.createElement('img');
img.onload = function () {
let ctx = canvas.getContext('2d');
// match size of image
canvas.width = img.naturalWidth || img.width;
canvas.height = img.naturalHeight || img.height;
// Copy the image contents to the canvas
ctx.drawImage(img, 0, 0);
// Get the data-URI formatted image
callback(null, canvas.toDataURL('image/png'));
};
img.ononerror = function () {
callback(new Error('FailedToLoadImage'));
};
// canvas is not supported
if (!canvas.getContext) {
setTimeout(callback, 0, new Error('CanvasIsNotSupported'));
} else {
img.setAttribute('crossOrigin', 'anonymous');
img.src = url;
};
};
which is based on this https://github.com/HenrikJoreteg/image-to-data-uri.js/blob/master/image-to-data-uri.js

Base64 png to Canvas

I'm struggeling with drawing a image on a Canvas.
What I have done so far is that I have inserted imagedata from a Canvas into a database.
The problem is that when I try to create an image of the data and draw it back on the Canvas later it does not draw the same, only some pixels may be drawen while the rest is just blank like nothing should be drawen there.
I'm getting the image data like this:
var CanvasData = document.getElementById('canvas');
CanvasData = CanvasData.toDataURL("image/png");
And drawing the image back on the Canvas like this (the data is stored in a database):
var result = xmlhttp.responseText;
var CanvasDraw = document.getElementById('canvas');
var ctxChange = CanvasDraw.getContext('2d');
imagedata = new Image();
imagedata.src = result;
imagedata.onload = function(){
ctxChange.drawImage(imagedata, 0, 0);
}
Here's a link to pastebin for an example of imagedata: http://pastebin.com/XGmV49k9
Result is the data that is returned from a AJAX call and is the same as what is stored in the database.
Thanks for any help.
Seems that error in this line:
imagedata.src = result;
Should be:
imagedata.src = CanvasData;

Categories

Resources