如何保存画布为 png 图像?

我有一个画布元素,其中有一个绘图,我想创建一个按钮,当点击,它将保存为 png 文件的图像。所以它应该打开保存,打开,关闭对话框..。

我用这个代码

var canvas = document.getElementById("myCanvas");
window.open(canvas.toDataURL("image/png"));

但是当我在 IE9中测试它的时候,一个新的窗口打开了: “网页无法显示” 网址是:

data:image/png;base64,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

有人知道怎么修吗?

264655 次浏览

try this:

var canvas = document.getElementById("alpha");
var dataURL = canvas.toDataURL("image/png");
var newTab = window.open('about:blank','image from canvas');
newTab.document.write("<img src='" + dataURL + "' alt='from canvas'/>");

This shows image from canvas on new page, but if you have open popup in new tab setting it shows about:blank in address bar.

EDIT:- though window.open("<img src='"+ canvas.toDataURL('image/png') +"'/>") does not work in FF or Chrome, following works though rendering is somewhat different from what is shown on canvas, I think transparency is the issue:

window.open(canvas.toDataURL('image/png'));

FileSaver.js should be able to help you here.

var canvas = document.getElementById("my-canvas");
// draw to canvas...
canvas.toBlob(function(blob) {
saveAs(blob, "pretty image.png");
});

I maybe discovered a better way for not forcing the user to right click and "save image as". Live draw the canvas base64 code into the href of the link and modify it so the download will start automatically. I don't know if it's universally browser compatible, but it should work with the main/new browsers.

var canvas = document.getElementById('your-canvas');
if (canvas.getContext) {
var C = canvas.getContext('2d');
}


$('#your-canvas').mousedown(function(event) {
// feel free to choose your event ;)


// just for example
// var OFFSET = $(this).offset();
// var x = event.pageX - OFFSET.left;
// var y = event.pageY - OFFSET.top;


// standard data to url
var imgdata = canvas.toDataURL('image/png');
// modify the dataUrl so the browser starts downloading it instead of just showing it
var newdata = imgdata.replace(/^data:image\/png/,'data:application/octet-stream');
// give the link the values it needs
$('a.linkwithnewattr').attr('download','your_pic_name.png').attr('href',newdata);


});

You can wrap the <a> around anything you want.

I used this solution to set the file name:

HTML:

<a href="#" id="downloader" onclick="download()" download="image.png">Download!</a>
<canvas id="canvas"></canvas>

JavaScript:

function download(){
document.getElementById("downloader").download = "image.png";
document.getElementById("downloader").href = document.getElementById("canvas").toDataURL("image/png").replace(/^data:image\/[^;]/, 'data:application/octet-stream');
}

Try this:

jQuery('body').after('<a id="Download" target="_blank">Click Here</a>');


var canvas = document.getElementById('canvasID');
var ctx = canvas.getContext('2d');


document.getElementById('Download').addEventListener('click', function() {
downloadCanvas(this, 'canvas', 'test.png');
}, false);


function downloadCanvas(link, canvasId, filename) {
link.href = document.getElementById(canvasId).toDataURL();
link.Download = filename;
}

You can just put this code in console in firefox or chrom and after changed your canvas tag ID in this above script and run this script in console.

After the execute this code you will see the link as text "click here" at bottom of the html page. click on this link and open the canvas drawing as a PNG image in new window save the image.

I had this problem and this is the best solution without any external or additional script libraries: In Javascript tags or file create this function: We assume here that canvas is your canvas:

function download(){
var download = document.getElementById("download");
var image = document.getElementById("canvas").toDataURL("image/png")
.replace("image/png", "image/octet-stream");
download.setAttribute("href", image);


}

In the body part of your HTML specify the button:

<a id="download" download="image.png"><button type="button" onClick="download()">Download</button></a>

This is working and download link looks like a button. Tested in Firefox and Chrome.

Submit a form that contains an input with value of canvas toDataURL('image/png') e.g

//JAVASCRIPT

    var canvas = document.getElementById("canvas");
var url = canvas.toDataUrl('image/png');

Insert the value of the url to your hidden input on form element.

//PHP

    $data = $_POST['photo'];
$data = str_replace('data:image/png;base64,', '', $data);
$data = base64_decode($data);
file_put_contents("i".  rand(0, 50).".png", $data);
var canvasId = chart.id + '-canvas';
var canvasDownloadId = chart.id + '-download-canvas';
var canvasHtml = Ext.String.format('<canvas id="{0}" width="{1}" height="{2}"></canvas><a id="{3}"/>',
canvasId,
chart.getWidth(),
chart.getHeight(),
canvasDownloadId);
var canvasElement = reportBuilder.add({ html: canvasHtml });


var canvas = document.getElementById(canvasId);


var canvasDownload = document.getElementById(canvasDownloadId);
canvasDownload.href = chart.getImage().data;
canvasDownload.download = 'chart';


canvasDownload.click();

Full Working HTML Code. Cut+Paste into new .HTML file:

Contains Two Examples:

  1. Canvas in HTML file.
  2. Canvas dynamically created with Javascript.

Tested In:

  1. Chrome
  2. Internet Explorer
  3. *Edge (title name does not show up)
  4. Firefox
  5. Opera

<!DOCTYPE HTML >
<html lang="en">
<head>
<meta charset="UTF-8">
<title> #SAVE_CANVAS_TEST# </title>
<meta
name   ="author"
content="John Mark Isaac Madison"
>
<!-- EMAIL: J4M4I5M7 -[AT]- Hotmail.com -->
</head>
<body>


<div id="about_the_code">
Illustrates:
<ol>
<li>How to save a canvas from HTML page.     </li>
<li>How to save a dynamically created canvas.</li>
</ol>
</div>


<canvas id="DOM_CANVAS"
width ="300"
height="300"
></canvas>


<div id="controls">
<button type="button" style="width:300px;"
onclick="obj.SAVE_CANVAS()">
SAVE_CANVAS ( Dynamically Made Canvas )
</button>


<button type="button" style="width:300px;"
onclick="obj.SAVE_CANVAS('DOM_CANVAS')">
SAVE_CANVAS ( Canvas In HTML Code )
</button>
</div>


<script>
var obj = new MyTestCodeClass();
function MyTestCodeClass(){


//Publically exposed functions:
this.SAVE_CANVAS = SAVE_CANVAS;


//:Private:
var _canvas;
var _canvas_id = "ID_OF_DYNAMIC_CANVAS";
var _name_hash_counter = 0;


//:Create Canvas:
(function _constructor(){
var D   = document;
var CE  = D.createElement.bind(D);
_canvas = CE("canvas");
_canvas.width = 300;
_canvas.height= 300;
_canvas.id    = _canvas_id;
})();


//:Before saving the canvas, fill it so
//:we can see it. For demonstration of code.
function _fillCanvas(input_canvas, r,g,b){
var ctx = input_canvas.getContext("2d");
var c   = input_canvas;


ctx.fillStyle = "rgb("+r+","+g+","+b+")";
ctx.fillRect(0, 0, c.width, c.height);
}


//:Saves canvas. If optional_id supplied,
//:will save canvas off the DOM. If not,
//:will save the dynamically created canvas.
function SAVE_CANVAS(optional_id){


var c = _getCanvas( optional_id );


//:Debug Code: Color canvas from DOM
//:green, internal canvas red.
if( optional_id ){
_fillCanvas(c,0,255,0);
}else{
_fillCanvas(c,255,0,0);
}


_saveCanvas( c );
}


//:If optional_id supplied, get canvas
//:from DOM. Else, get internal dynamically
//:created canvas.
function _getCanvas( optional_id ){
var c = null; //:canvas.
if( typeof optional_id == "string"){
var id = optional_id;
var  d = document;
var c  = d.getElementById( id );
}else{
c = _canvas;
}
return c;
}


function _saveCanvas( canvas ){
if(!window){ alert("[WINDOW_IS_NULL]"); }


//:We want to give the window a unique
//:name so that we can save multiple times
//:without having to close previous
//:windows.
_name_hash_counter++              ;
var NHC = _name_hash_counter      ;
var URL = 'about:blank'           ;
var name= 'UNIQUE_WINDOW_ID' + NHC;
var w=window.open( URL, name )    ;


if(!w){ alert("[W_IS_NULL]");}


//:Create the page contents,
//:THEN set the tile. Order Matters.
var DW = ""                        ;
DW += "<img src='"                 ;
DW += canvas.toDataURL("image/png");
DW += "' alt='from canvas'/>"      ;
w.document.write(DW)               ;
w.document.title = "NHC"+NHC       ;


}


}//:end class


</script>


</body>
<!-- In IE: Script cannot be outside of body.  -->
</html>

To accomodate all three points:

  • button
  • save the image as a png file
  • open up the save, open, close dialog box

The file dialog is a setting in the browser.

For the button/save part assign the following function, boiled down from other answers, to your buttons onclick:

function DownloadCanvasAsImage(){
let downloadLink = document.createElement('a');
downloadLink.setAttribute('download', 'CanvasAsImage.png');
let canvas = document.getElementById('myCanvas');
let dataURL = canvas.toDataURL('image/png');
let url = dataURL.replace(/^data:image\/png/,'data:application/octet-stream');
downloadLink.setAttribute('href', url);
downloadLink.click();
}

Example on Codepen

Another, somewhat cleaner, approach is using Canvas.toBlob():

function DownloadCanvasAsImage(){
let downloadLink = document.createElement('a');
downloadLink.setAttribute('download', 'CanvasAsImage.png');
let canvas = document.getElementById('myCanvas');
canvas.toBlob(function(blob) {
let url = URL.createObjectURL(blob);
downloadLink.setAttribute('href', url);
downloadLink.click();
});
}

Example on Codepen

Neither solution is 100% cross browser compatible, so check the client

I really like Tovask's answer but it doesn't work due to the function having the name download (this answer explains why). I also don't see the point in replacing "data:image/..." with "data:application/...".

The following code has been tested in Chrome and Firefox and seems to work fine in both.

JavaScript:

function prepDownload(a, canvas, name) {
a.download = name
a.href = canvas.toDataURL()
}

HTML:

<a href="#" onclick="prepDownload(this, document.getElementById('canvasId'), 'imgName.png')">Download</a>
<canvas id="canvasId"></canvas>

My solution via vue and async support

async downloadImage () {
const canvas = this.$refs.canvas
const blob = await new Promise(resolve => canvas.toBlob(resolve))


const downloadLink = document.createElement('a')
downloadLink.href = window.URL.createObjectURL(blob)
downloadLink.download = 'mycanvasimage.png'
downloadLink.click()
}