204

I'm currently building a HTML5 web app/Phonegap native app and I can't seem to figure out how to save my canvas as an image with canvas.toDataURL(). Can somebody help me out?

Here's the code, what's wrong with it?

//My canvas was named "canvasSignature"

JavaScript:


function putImage()
{
  var canvas1 = document.getElementById("canvasSignature");        
  if (canvas1.getContext) {
     var ctx = canvas1.getContext("2d");                
     var myImage = canvas1.toDataURL("image/png");      
  }
  var imageElement = document.getElementById("MyPix");  
  imageElement.src = myImage;                           

}  

HTML5:


<div id="createPNGButton">
    <button onclick="putImage()">Save as Image</button>        
</div>
ROMANIA_engineer
  • 54,432
  • 29
  • 203
  • 199
Wardenclyffe
  • 2,401
  • 3
  • 19
  • 17
  • 6
    OP's question has not been answered. He clearly said this is for Phonegap / iPad. The answers given are for saving on a desktop browser. – so001 Jul 16 '12 at 17:40
  • 1
    Not sure about phonegap, but I've done this from scratch in native iOS using JavaScript on the other end, I capture the data with `.toDataURL()`, then use window.location to point the browser to `appname://[data url]`. On the app end, the UIWebView has a delegate method that says whether or not it should load a page. I listen for `appname://` and break it down when it comes in, deny the page load and capture the data url in a native string... how familiar are you with actual iOS/Objective C code? – Doug Oct 24 '12 at 23:19

14 Answers14

193

Here is some code. without any error.

var image = canvas.toDataURL("image/png").replace("image/png", "image/octet-stream");  // here is the most important part because if you dont replace you will get a DOM 18 exception.


window.location.href=image; // it will save locally
Hauleth
  • 22,873
  • 4
  • 61
  • 112
user1874941
  • 3,013
  • 4
  • 20
  • 31
  • 1
    Except in IE9 standards mode: "Some content or files on this webpage require a program that you don't have installed." Internet Explorer 8 and above only supports data URIs for images in CSS, , and : https://developer.mozilla.org/en-US/docs/data_URIs – Cees Timmerman Jun 05 '13 at 12:37
  • 66
    works fine. how can I change the name of downloaded file? it's coming just "download" and without extension. thanks! – Leabdalla Oct 18 '13 at 14:23
  • 3
    In Chrome this crashes the browser. If I display the image in an image tag, it does work but the right-click menu is greyed out - so I still can't save the image. – Kokodoko May 15 '14 at 16:39
  • This works great... But in Android (Default browser in Galaxy S3) it just doesn't download the image, but i get the message "Downloading..." forever. – Habrashat Jul 28 '14 at 06:05
  • I have save issue some one can help me see this link :http://stackoverflow.com/questions/25131763/how-to-save-the-canvas-text-image-in-gallery?noredirect=1#comment39171750_25131763 – Ramesh Somalagari Aug 11 '14 at 14:47
  • @CeesTimmerman IE8 doesn't support `canvas` so there's no point to even think about it to begin with. – hipkiss Jun 20 '16 at 14:42
  • Michael, hipkiss, Cees Timmerman : Nothing works on IE. As developer, we must have in mind to program to IE and to the rest. – Magno C May 03 '17 at 12:31
  • How can i change the path to save the image? I wish to store it in my app folder. – Vishal May 06 '17 at 10:51
  • @Leabdalla check out my answer for that – Thomas Wagenaar Jun 11 '17 at 19:29
  • 2
    Doesn't work under chrome 76/windows7. it locate to `about:blank#blocked`. – duXing Aug 19 '19 at 14:46
  • security exception in 2020 ,can't use todataUrl – TAHA SULTAN TEMURI Jan 19 '20 at 19:35
  • For security exception: img.setAttribute('crossorigin', 'anonymous'); – Extrange planet Feb 27 '22 at 01:06
  • To download with extension: https://stackoverflow.com/a/56185896/4906752 – Extrange planet Feb 27 '22 at 01:30
78

This solution allows you to change the name of the downloaded file:

HTML:

<a id="link"></a>

JAVASCRIPT:

  var link = document.getElementById('link');
  link.setAttribute('download', 'MintyPaper.png');
  link.setAttribute('href', canvas.toDataURL("image/png").replace("image/png", "image/octet-stream"));
  link.click();
Thomas Wagenaar
  • 6,489
  • 5
  • 30
  • 73
40

You can try this; create a dummy HTML anchor, and download the image from there like...

// Convert canvas to image
document.getElementById('btn-download').addEventListener("click", function(e) {
    var canvas = document.querySelector('#my-canvas');

    var dataURL = canvas.toDataURL("image/jpeg", 1.0);

    downloadImage(dataURL, 'my-canvas.jpeg');
});

// Save | Download image
function downloadImage(data, filename = 'untitled.jpeg') {
    var a = document.createElement('a');
    a.href = data;
    a.download = filename;
    document.body.appendChild(a);
    a.click();
}
Woold
  • 783
  • 12
  • 23
bmatovu
  • 3,756
  • 1
  • 35
  • 37
25

You can use canvas2image to prompt for download.

I had the same issue, here's a simple example that both adds the image to the page and forces the browser to download it:

<html>
    <head>
        <script src="http://hongru.github.io/proj/canvas2image/canvas2image.js"></script>
        <script>
            function draw(){
                var canvas = document.getElementById("thecanvas");
                var ctx = canvas.getContext("2d");
                ctx.fillStyle = "rgba(125, 46, 138, 0.5)";
                ctx.fillRect(25,25,100,100); 
                ctx.fillStyle = "rgba( 0, 146, 38, 0.5)";
                ctx.fillRect(58, 74, 125, 100);
            }

            function to_image(){
                var canvas = document.getElementById("thecanvas");
                document.getElementById("theimage").src = canvas.toDataURL();
                Canvas2Image.saveAsPNG(canvas);
            }
        </script>
    </head>
    <body onload="draw()">
        <canvas width=200 height=200 id="thecanvas"></canvas>
        <div><button onclick="to_image()">Draw to Image</button></div>
        <image id="theimage"></image>
    </body>
</html>
Suresh Atta
  • 120,458
  • 37
  • 198
  • 307
SColvin
  • 11,584
  • 6
  • 57
  • 71
  • 3
    Just tried it out, it will save a file with no name nor extension in Chrome. – malber Jul 11 '13 at 07:25
  • as mentioned here http://www.nihilogic.dk/labs/canvas2image/ setting the file name doesn't seem to be possible: "It would be really neat if somehow a filename could be attached to the data, but I've found no way to do that. For now, you have to specify the filename yourself." – SColvin Sep 13 '13 at 00:14
  • 2
    The link you posted is broken – Oliver Nybroe Nov 16 '14 at 17:04
  • To download with file name and extension: https://stackoverflow.com/a/56185896/4906752 – Extrange planet Feb 27 '22 at 01:30
10

I created a small library that does this (along with some other handy conversions). It's called reimg, and it's really simple to use.

ReImg.fromCanvas(yourCanvasElement).toPng()

gillyb
  • 8,760
  • 8
  • 53
  • 80
  • Cool library. Thanks. There's a small issue in the example documentation though. I opened an issue on Github to bring it to your attention. – jmknoll Nov 23 '15 at 09:16
8

Similar to 1000Bugy's answer but simpler because you don't have to make an anchor on the fly and dispatch a click event manually (plus an IE fix).

If you make your download button an anchor you can highjack it right before the default anchor functionality is run. So onAnchorClick you can set the anchor href to the canvas base64 image and the anchor download attribute to whatever you want to name your image.

This does not work in (the current) IE because it doesn't implement the download attribute and prevents download of data uris. But this can be fixed by using window.navigator.msSaveBlob instead.

So your anchor click event handler would be as followed (where anchor, canvas and fileName are scope lookups):

function onClickAnchor(e) {
  if (window.navigator.msSaveBlob) {
    window.navigator.msSaveBlob(canvas.msToBlob(), fileName);
    e.preventDefault();
  } else {
    anchor.setAttribute('download', fileName);
    anchor.setAttribute('href', canvas.toDataURL());
  }
}

Here's a fiddle

Community
  • 1
  • 1
Sjeiti
  • 2,468
  • 1
  • 31
  • 33
7

This work for me: (Only google chrome)

<html>
<head>
    <script>
            function draw(){
                var canvas = document.getElementById("thecanvas");
                var ctx = canvas.getContext("2d");
                ctx.fillStyle = "rgba(125, 46, 138, 0.5)";
                ctx.fillRect(25,25,100,100);
                ctx.fillStyle = "rgba( 0, 146, 38, 0.5)";
                ctx.fillRect(58, 74, 125, 100);
            }

            function downloadImage()
            {
                var canvas = document.getElementById("thecanvas");
                var image = canvas.toDataURL();

                var aLink = document.createElement('a');
                var evt = document.createEvent("HTMLEvents");
                evt.initEvent("click");
                aLink.download = 'image.png';
                aLink.href = image;
                aLink.dispatchEvent(evt);
            }
    </script>
</head>
<body onload="draw()">
    <canvas width=200 height=200 id="thecanvas"></canvas>
    <div><button onclick="downloadImage()">Download</button></div>
    <image id="theimage"></image>
</body>
</html>
1000Bugy
  • 105
  • 1
  • 8
2

I create simple typescript function for this purpose, just delete types to use with javascript.

 //ImagePath == canvas.toDataUrl()
private saveImage(imagePath: string, imageName: string ){
    const link = document.createElement('a');
    link.style.display = 'none';
    document.body.appendChild(link)
    link.setAttribute('download', imageName + '.png');
    link.setAttribute('href', imagePath.replace("image/png", "image/octet-stream"));
    link.click();
}

 //function call
 saveImage(canvas.toDataURL(), "myName")
Vít Zadina
  • 168
  • 8
1

Instead of imageElement.src = myImage; you should use window.location = myImage;

And even after that the browser will display the image itself. You can right click and use "Save Link" for downloading the image.

Check this link for more information.

Hakan Serce
  • 11,198
  • 3
  • 29
  • 48
  • Wow, thanks, that really helped a lot:) But how do you get a pop up save box, so that someone can save to a specific destination (like their Android images folder)? – Wardenclyffe May 20 '12 at 12:11
  • It depends on the specific browser. Android browser usually download files to a specific folder directly, for instace. – Hakan Serce May 20 '12 at 12:20
  • In Chrome this works, but the right-click menu is greyed out. When trying to 'drag' the image out of the browser, Chrome crashes. – Kokodoko May 15 '14 at 16:41
  • https://groups.google.com/a/chromium.org/forum/#!topic/blink-dev/GbVcuwg_QjM Opening data URL like this is going to be blocked it looks like. – KeyOfJ Jun 19 '17 at 20:09
1

You need to create an Image object that will store the base 64 image of the canvas. That way your image imageElement can then take its src attribute value.

function putImage(){
   var canvas1 = document.getElementById("canvasSignature");        
   
   var imageObject = new Image();
   imageObject.src = canvas1.toDataURL("image/png");      

   var imageElement = document.getElementById("MyPix");  
   imageElement.src = imageObject.src;                           
} 

Automatically saving the image

You can automatically save the image by creating a link and attaching the src value as the href of the link. Then simulate a click using the click() method in JavaScript.

function putImage(){
       var canvas1 = document.getElementById("canvasSignature");        
       
       var imageObject = new Image();
       imageObject.src = canvas1.toDataURL("image/png");      

       var imageElement = document.getElementById("MyPix");  
       imageElement.src = imageObject.src;    
       

       // Saving it locally automatically
       let link = document.createElement("a");
       link.setAttribute('download', "download")
       link.href= imageElement.src
       link.click()               
    } 
 
1

try the following line after ctx

var img = new Image();
img.crossOrigin = 'anonymous';

I can show you a small example

You can create 2 functions

  1. To draw an image on the canvas from a url (could be local/remote).

         function loadCanvas(){
         var c = document.getElementById("mycanvas");
         var ctx = c.getContext("2d");
         var img = new Image();
         img.crossOrigin = 'anonymous';
         img.addEventListener("load",()=>{
              ctx.drawImage(img, 0, 0, 300, 450);
           });
          //i have chosen a random image from the internet to display
         img.src = 'https://images.squarespace-cdn.com/content/v1/592df00e3a0411d38a6c0e88/1508276785945-S2PHOFE36SU6NN6K8F3Y/70db5-sunsetideahousesunsetideahouse.jpg';
    
          }
    
  2. To save/publish that image or display in img tag

        function copyimage(){
            var c = document.getElementById("mycanvas");
            var datanew = c.toDataURL("image/jpeg");
            $(".imagecopy").html('<img src="'+datanew+'" width="300px" height="450px" />');
          }
    

Click here to see the complete code

Riya Singh
  • 107
  • 1
  • 8
  • 1
    Thank you for this code snippet, which might provide some limited, immediate help. A [proper explanation](https://meta.stackexchange.com/q/114762/349538) would greatly improve its long-term value by showing why this is a good solution to the problem and would make it more useful to future readers with other, similar questions. Please [edit] your answer to add some explanation, including the assumptions you’ve made. – PeterS Dec 02 '22 at 17:32
  • hi, i have just edited my post and have even provided a link with a working example on jsfiddle. – Riya Singh Dec 03 '22 at 15:01
0

You cannot use the methods previously mentioned to download an image when running in Cordova. You will need to use the Cordova File Plugin. This will allow you to pick where to save it and leverage different persistence settings. Details here: https://cordova.apache.org/docs/en/latest/reference/cordova-plugin-file/

Alternatively, you can convert the image to base64 then store the string in localStorage but this will fill your quota pretty quickly if you have many images or high-res.

BigBalli
  • 762
  • 1
  • 6
  • 10
  • is there a problem in saving the base64 string in a a hidden text field if it is not meant to be kept for ever? just asking. – Riya Singh Dec 04 '22 at 14:53
0

One can also use element reference to downloaded image:

HTML:

<a download style="display:none" ref="imageDownloadRef"></a>

JAVASCRIPT:

  this.$refs['imageDownloadRef'].href = this.canvas.toDataURL({
  format: 'jpeg',
  quality: 1.0,
  })
  const imageDownloadLink = this.$refs.imageDownloadRef as HTMLElement
  imageDownloadLink.click()
Yogesh Yadav
  • 723
  • 6
  • 21
-9

@Wardenclyffe and @SColvin, you both are trying to save image using the canvas, not by using canvas's context. both you should try to ctx.toDataURL(); Try This:

var canvas1 = document.getElementById("yourCanvasId");  <br>
var ctx = canvas1.getContext("2d");<br>
var img = new Image();<br>
img.src = ctx.toDataURL('image/png');<br>
ctx.drawImage(img,200,150);<br>

Also you may refer to following links:

http://tutorials.jenkov.com/html5-canvas/todataurl.html

http://www.w3.org/TR/2012/WD-html5-author-20120329/the-canvas-element.html#the-canvas-element

Community
  • 1
  • 1
  • Uncaught TypeError: Object # has no method 'toDataURL'. – Brandon Feb 25 '14 at 14:19
  • 2
    This is wrong as the context element doesn't have a `toDataURL` function: https://developer.mozilla.org/en-US/docs/Web/API/CanvasRenderingContext2D. You want to call `toDataURL` on the canvas element: https://developer.mozilla.org/en-US/docs/Web/API/HTMLCanvasElement/getContext – Crashalot Feb 14 '20 at 22:59