是否可以捕获或打印html画布中显示的图像或pdf?
我想通过画布生成图像,并能够从该图像生成一个png。
答案 0 :(得分:682)
糟糕。原始答案是针对类似问题的。这已被修改:
var canvas = document.getElementById("mycanvas");
var img = canvas.toDataURL("image/png");
使用IMG中的值,您可以将其写为新图像,如下所示:
document.write('<img src="'+img+'"/>');
答案 1 :(得分:112)
HTML5提供了Canvas.toDataURL(mimetype),它在Opera,Firefox和Safari 4 beta中实现。但是,有许多安全限制(主要是将内容从另一个来源绘制到画布上)。
所以你不需要额外的库。
e.g。
<canvas id=canvas width=200 height=200></canvas>
<script>
window.onload = function() {
var canvas = document.getElementById("canvas");
var context = canvas.getContext("2d");
context.fillStyle = "green";
context.fillRect(50, 50, 100, 100);
// no argument defaults to image/png; image/jpeg, etc also work on some
// implementations -- image/png is the only one that must be supported per spec.
window.location = canvas.toDataURL("image/png");
}
</script>
理论上,这应该创建然后导航到中间带有绿色方块的图像,但我还没有测试过。
答案 2 :(得分:41)
我认为我会稍微扩展这个问题的范围,并提供一些有用的花絮。
为了将画布作为图像,您应该执行以下操作:
var canvas = document.getElementById("mycanvas");
var image = canvas.toDataURL("image/png");
您可以使用此功能将图像写入页面:
document.write('<img src="'+image+'"/>');
其中“image / png”是mime类型(png是唯一必须支持的类型)。如果您想要一组受支持的类型,您可以按照以下方式执行某些操作:
var imageMimes = ['image/png', 'image/bmp', 'image/gif', 'image/jpeg', 'image/tiff']; //Extend as necessary
var acceptedMimes = new Array();
for(i = 0; i < imageMimes.length; i++) {
if(canvas.toDataURL(imageMimes[i]).search(imageMimes[i])>=0) {
acceptedMimes[acceptedMimes.length] = imageMimes[i];
}
}
您只需每页运行一次 - 它永远不会在页面的生命周期中发生变化。
如果您希望让用户在保存文件时下载该文件,您可以执行以下操作:
var canvas = document.getElementById("mycanvas");
var image = canvas.toDataURL("image/png").replace("image/png", "image/octet-stream"); //Convert image to 'octet-stream' (Just a download, really)
window.location.href = image;
如果你使用不同的mime类型,请务必更改image / png的两个实例,而不是image / octet-stream。 还值得一提的是,如果在渲染画布时使用任何跨域资源,则在尝试使用toDataUrl方法时会遇到安全性错误。
答案 3 :(得分:30)
function exportCanvasAsPNG(id, fileName) {
var canvasElement = document.getElementById(id);
var MIME_TYPE = "image/png";
var imgURL = canvasElement.toDataURL(MIME_TYPE);
var dlLink = document.createElement('a');
dlLink.download = fileName;
dlLink.href = imgURL;
dlLink.dataset.downloadurl = [MIME_TYPE, dlLink.download, dlLink.href].join(':');
document.body.appendChild(dlLink);
dlLink.click();
document.body.removeChild(dlLink);
}
答案 4 :(得分:21)
我会使用“wkhtmltopdf”。它工作得很好。它使用webkit引擎(用于Chrome,Safari等),它非常易于使用:
wkhtmltopdf stackoverflow.com/questions/923885/ this_question.pdf
就是这样!
答案 5 :(得分:15)
如果您通过服务器下载(这样您可以命名/转换/后处理/等等文件),这里有一些帮助:
- 使用toDataURL
- 设置标题
$filename = "test.jpg"; //or png
header('Content-Description: File Transfer');
if($msie = !strstr($_SERVER["HTTP_USER_AGENT"],"MSIE")==false)
header("Content-type: application/force-download");else
header("Content-type: application/octet-stream");
header("Content-Disposition: attachment; filename=\"$filename\"");
header("Content-Transfer-Encoding: binary");
header("Expires: 0"); header("Cache-Control: must-revalidate");
header("Pragma: public");
- 创建图片
$data = $_POST['data'];
$img = imagecreatefromstring(base64_decode(substr($data,strpos($data,',')+1)));
- 出口图片as JPEG
$width = imagesx($img);
$height = imagesy($img);
$output = imagecreatetruecolor($width, $height);
$white = imagecolorallocate($output, 255, 255, 255);
imagefilledrectangle($output, 0, 0, $width, $height, $white);
imagecopy($output, $img, 0, 0, 0, 0, $width, $height);
imagejpeg($output);
exit();
imagesavealpha($img, true);
imagepng($img);
die($img);
答案 6 :(得分:8)
另一个有趣的解决方案是PhantomJS。 它是一个使用JavaScript或CoffeeScript的无头WebKit脚本。
其中一个用例是屏幕截图:您可以通过编程方式捕获Web内容,包括SVG和Canvas和/或使用缩略图预览创建网站屏幕截图。
最佳切入点是screen capture维基页面。
这是极地时钟的一个很好的例子(来自RaphaelJS):
>phantomjs rasterize.js http://raphaeljs.com/polar-clock.html clock.png
您要将网页呈现为PDF吗?
> phantomjs rasterize.js 'http://en.wikipedia.org/w/index.php?title=Jakarta&printable=yes' jakarta.pdf
答案 7 :(得分:3)
如果您正在使用jQuery,很多人都会这样做,那么您将实现所接受的答案:
var canvas = $("#mycanvas")[0];
var img = canvas.toDataURL("image/png");
$("#elememt-to-write-to").html('<img src="'+img+'"/>');
答案 8 :(得分:1)
您可以使用jspdf将画布捕获到图像或pdf中,如下所示:
var imgData = canvas.toDataURL('image/png');
var doc = new jsPDF('p', 'mm');
doc.addImage(imgData, 'PNG', 10, 10);
doc.save('sample-file.pdf');
答案 9 :(得分:0)
这是另一种方式,没有字符串,尽管我真的不知道它是否更快。而不是toDataURL(如此处所有问题所建议)。在我的情况下,由于我需要一个数组缓冲区或视图,因此想阻止dataUrl / base64。因此,HTMLCanvasElement中的另一个方法是toBlob
。 (TypeScript函数):
export function canvasToArrayBuffer(canvas: HTMLCanvasElement, mime: string): Promise<ArrayBuffer> {
return new Promise((resolve, reject) => canvas.toBlob(async (d) => {
if (d) {
const r = new FileReader();
r.addEventListener('loadend', e => {
const ab = r.result;
if (ab) {
resolve(ab as ArrayBuffer);
}
else {
reject(new Error('Expected FileReader result'));
}
}); r.addEventListener('error', e => {
reject(e)
});
r.readAsArrayBuffer(d);
}
else {
reject(new Error('Expected toBlob() to be defined'));
}
}, mime));
}
Blob的另一个优点是,您可以创建ObjectUrl来将数据表示为文件,类似于HTMLInputFile的“ files”成员。更多信息:
https://developer.mozilla.org/es/docs/Web/API/HTMLCanvasElement/toBlob
答案 10 :(得分:0)
简单的答案是获取它的blob并将img src设置为该blob的新对象URL,然后使用某些库将该图像添加到PDF,例如
var ok = document.createElement("canvas")
ok.width = 400
ok.height = 140
var ctx = ok.getContext("2d");
for(let k = 0; k < ok.height; k++)
(
k
%
Math.floor(
(
Math.random()
) *
10
)
==
0
) && (y => {
for(var i = 0; i < ok.width; i++) {
if(i % 25 == 0) {
ctx.globalAlpha = Math.random()
ctx.fillStyle = (
"rgb(" +
Math.random() * 255 + "," +
Math.random() * 255 + "," +
Math.random() * 255 + ")"
);
(wdth =>
ctx.fillRect(
Math.sin(
i * Math.PI / 180
) *
Math.random() *
ok.width,
Math.cos(
i * Math.PI / 180,
) * wdth + y,
wdth,
wdth
)
)(15)
}
}
})(k)
ok.toBlob(blob => {
k.src = URL.createObjectURL(blob)
})
<img id=k>
或者,如果您想使用低级字节数据,则可以获取画布的原始字节,然后根据文件规范,将原始图像数据写入数据的必要字节中。您只需要调用ctx.getImageData(0, 0, ctx.canvas.widht, ctx.canvas.height)
即可获取原始图像数据,然后根据文件规范将其写入该文件中。
答案 11 :(得分:0)
如果你想嵌入画布,你可以使用这个片段
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
</head>
<body>
<canvas id=canvas width=200 height=200></canvas>
<iframe id='img' width=200 height=200></iframe>
<script>
window.onload = function() {
var canvas = document.getElementById("canvas");
var context = canvas.getContext("2d");
context.fillStyle = "green";
context.fillRect(50, 50, 100, 100);
document.getElementById('img').src = canvas.toDataURL("image/jpeg");
console.log(canvas.toDataURL("image/jpeg"));
}
</script>
</body>
</html>
答案 12 :(得分:0)
重点是
<块引用>canvas.toDataURL(type, quality)
我想为像我这样想要将 SVG 保存为 PNG 的人提供一个示例(如果您愿意,也可以添加一些文本),这可能来自在线资源或字体很棒的图标等。
100% javascript,没有其他 3-rd 库。
<script>
(() => {
window.onload = () => {
// Test 1: SVG from Online
const canvas = new Canvas(650, 500)
// canvas.DrawGrid() // If you want to show grid, you can use it.
const svg2img = new SVG2IMG(canvas.canvas, "https://upload.wikimedia.org/wikipedia/commons/b/bd/Test.svg")
svg2img.AddText("Hello", 100, 250, {mode: "fill", color: "yellow", alpha: 0.8})
svg2img.AddText("world", 200, 250, {mode: "stroke", color: "red"})
svg2img.AddText("!", 280, 250, {color: "#f700ff", size: "72px"})
svg2img.Build("Test.png")
// Test 2: URI.data
const canvas2 = new Canvas(180, 180)
const uriData = "data:image/svg+xml;base64,PHN2ZyBjbGFzcz0ic3ZnLWlubGluZS0tZmEgZmEtc21pbGUtd2luayBmYS13LTE2IiBhcmlhLWhpZGRlbj0idHJ1ZSIgZm9jdXNhYmxlPSJmYWxzZSIgZGF0YS1wcmVmaXg9ImZhciIgZGF0YS1pY29uPSJzbWlsZS13aW5rIiByb2xlPSJpbWciIHhtbG5zPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwL3N2ZyIgdmlld0JveD0iMCAwIDQ5NiA1MTIiIGRhdGEtZmEtaTJzdmc9IiI+PHBhdGggZmlsbD0iY3VycmVudENvbG9yIiBkPSJNMjQ4IDhDMTExIDggMCAxMTkgMCAyNTZzMTExIDI0OCAyNDggMjQ4IDI0OC0xMTEgMjQ4LTI0OFMzODUgOCAyNDggOHptMCA0NDhjLTExMC4zIDAtMjAwLTg5LjctMjAwLTIwMFMxMzcuNyA1NiAyNDggNTZzMjAwIDg5LjcgMjAwIDIwMC04OS43IDIwMC0yMDAgMjAwem0xMTcuOC0xNDYuNGMtMTAuMi04LjUtMjUuMy03LjEtMzMuOCAzLjEtMjAuOCAyNS01MS41IDM5LjQtODQgMzkuNHMtNjMuMi0xNC4zLTg0LTM5LjRjLTguNS0xMC4yLTIzLjctMTEuNS0zMy44LTMuMS0xMC4yIDguNS0xMS41IDIzLjYtMy4xIDMzLjggMzAgMzYgNzQuMSA1Ni42IDEyMC45IDU2LjZzOTAuOS0yMC42IDEyMC45LTU2LjZjOC41LTEwLjIgNy4xLTI1LjMtMy4xLTMzLjh6TTE2OCAyNDBjMTcuNyAwIDMyLTE0LjMgMzItMzJzLTE0LjMtMzItMzItMzItMzIgMTQuMy0zMiAzMiAxNC4zIDMyIDMyIDMyem0xNjAtNjBjLTI1LjcgMC01NS45IDE2LjktNTkuOSA0Mi4xLTEuNyAxMS4yIDExLjUgMTguMiAxOS44IDEwLjhsOS41LTguNWMxNC44LTEzLjIgNDYuMi0xMy4yIDYxIDBsOS41IDguNWM4LjUgNy40IDIxLjYuMyAxOS44LTEwLjgtMy44LTI1LjItMzQtNDIuMS01OS43LTQyLjF6Ij48L3BhdGg+PC9zdmc+"
const svg2img2 = new SVG2IMG(canvas2.canvas, uriData)
svg2img2.Build("SmileWink.png")
// Test 3: Exists SVG
ImportFontAwesome()
const range = document.createRange()
const fragSmile = range.createContextualFragment(`<i class="far fa-smile" style="background-color:black;color:yellow"></i>`)
document.querySelector(`body`).append(fragSmile)
// use MutationObserver wait the fontawesome convert ``<i class="far fa-smile"></i>`` to SVG. If you write the element in the HTML, then you can skip this hassle way.
const observer = new MutationObserver((mutationRecordList, observer) => {
for (const mutation of mutationRecordList) {
switch (mutation.type) {
case "childList":
const targetSVG = mutation.target.querySelector(`svg`)
if (targetSVG !== null) {
const canvas3 = new Canvas(64, 64) // ? Focus here. The part of the observer is not important.
const svg2img3 = new SVG2IMG(canvas3.canvas, SVG2IMG.Convert2URIData(targetSVG))
svg2img3.Build("Smile.png")
targetSVG.remove() // This SVG is created by font-awesome, and it's an extra element. I don't want to see it.
observer.disconnect()
return
}
}
}
})
observer.observe(document.querySelector(`body`), {childList: true})
}
})()
class SVG2IMG {
/**
* @param {HTMLCanvasElement} canvas
* @param {string} src "http://.../xxx.svg" or "data:image/svg+xml;base64,${base64}"
* */
constructor(canvas, src) {
this.canvas = canvas;
this.context = this.canvas.getContext("2d")
this.src = src
this.addTextList = []
}
/**
* @param {HTMLElement} node
* @param {string} mediaType: https://en.wikipedia.org/wiki/Media_type#Common_examples_%5B10%5D
* @see https://en.wikipedia.org/wiki/List_of_URI_schemes
* */
static Convert2URIData(node, mediaType = 'data:image/svg+xml') {
const base64 = btoa(node.outerHTML)
return `${mediaType};base64,${base64}`
}
/**
* @param {string} text
* @param {int} x
* @param {int} y
* @param {"stroke"|"fill"} mode
* @param {string} size, "30px"
* @param {string} font, example: "Arial"
* @param {string} color, example: "#3ae016" or "yellow"
* @param {int} alpha, 0.0 (fully transparent) to 1.0 (fully opaque) // https://developer.mozilla.org/en-US/docs/Web/API/Canvas_API/Tutorial/Applying_styles_and_colors#transparency
* */
AddText(text, x, y, {mode = "fill", size = "32px", font = "Arial", color = "black", alpha = 1.0}) {
const drawFunc = (text, x, y, mode, font) => {
return () => {
// https://www.w3schools.com/graphics/canvas_text.asp
// https://developer.mozilla.org/en-US/docs/Web/API/CanvasRenderingContext2D/fillText
const context = this.context
const originAlpha = context.globalAlpha
context.globalAlpha = alpha
context.font = `${size} ${font}`
switch (mode) {
case "fill":
context.fillStyle = color
context.fillText(text, x, y)
break
case "stroke":
context.strokeStyle = color
context.strokeText(text, x, y)
break
default:
throw Error(`Unknown mode:${mode}`)
}
context.globalAlpha = originAlpha
}
}
this.addTextList.push(drawFunc(text, x, y, mode, font))
}
/**
* @description When the build is finished, you can click the filename to download the PNG or mouse enters to copy PNG to the clipboard.
* */
Build(filename = "download.png") {
const img = new Image()
img.src = this.src
img.crossOrigin = "anonymous" // Fixes: Tainted canvases may not be exported
img.onload = (event) => {
this.context.drawImage(event.target, 0, 0)
for (const drawTextFunc of this.addTextList) {
drawTextFunc()
}
// create a "a" node for download
const a = document.createElement('a')
document.querySelector('body').append(a)
a.innerText = filename
a.download = filename
const quality = 1.0
// a.target = "_blank"
a.href = this.canvas.toDataURL("image/png", quality)
a.append(this.canvas)
}
this.canvas.onmouseenter = (event) => {
// set background to white. Otherwise, background-color is black.
this.context.globalCompositeOperation = "destination-over" // https://developer.mozilla.org/en-US/docs/Web/API/CanvasRenderingContext2D/globalCompositeOperation // https://www.w3schools.com/tags/canvas_globalcompositeoperation.asp
this.context.fillStyle = "rgb(255,255,255)"
this.context.fillRect(0, 0, this.canvas.width, this.canvas.height)
this.canvas.toBlob(blob => navigator.clipboard.write([new ClipboardItem({'image/png': blob})])) // copy to clipboard
}
}
}
class Canvas {
/**
* @description for do something like that: ``<canvas width="" height=""></>canvas>``
**/
constructor(w, h) {
const canvas = document.createElement("canvas")
document.querySelector(`body`).append(canvas)
this.canvas = canvas;
[this.canvas.width, this.canvas.height] = [w, h]
}
/**
* @description If your SVG is large, you may want to know which part is what you wanted.
* */
DrawGrid(step = 100) {
const ctx = this.canvas.getContext('2d')
const w = this.canvas.width
const h = this.canvas.height
// Draw the vertical line.
ctx.beginPath();
for (let x = 0; x <= w; x += step) {
ctx.moveTo(x, 0);
ctx.lineTo(x, h);
}
// set the color of the line
ctx.strokeStyle = 'rgba(255,0,0, 0.5)'
ctx.lineWidth = 1
ctx.stroke();
// Draw the horizontal line.
ctx.beginPath();
for (let y = 0; y <= h; y += step) {
ctx.moveTo(0, y)
ctx.lineTo(w, y)
}
ctx.strokeStyle = 'rgba(128, 128, 128, 0.5)'
ctx.lineWidth = 5
ctx.stroke()
}
}
function ImportFontAwesome() {
const range = document.createRange()
const frag = range.createContextualFragment(`
<link rel="stylesheet" type="text/css" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/5.15.2/css/all.min.css" integrity="sha512-HK5fgLBL+xu6dm/Ii3z4xhlSUyZgTT9tuc/hSrtw6uzJOvgRr2a9jyxxT1ely+B+xFAmJKVSTbpM/CuL7qxO8w==" crossorigin="anonymous" />
<script src="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/5.15.2/js/all.min.js" integrity="sha512-UwcC/iaz5ziHX7V6LjSKaXgCuRRqbTp1QHpbOJ4l1nw2/boCfZ2KlFIqBUA/uRVF0onbREnY9do8rM/uT/ilqw==" crossorigin="anonymous"/>
`)
document.querySelector("head").append(frag)
}
</script>
如果你想在stackoverflow上运行并在图片上移动鼠标可能会出错
<块引用>DOMException:剪贴板 API 由于应用于当前文档的权限策略而被阻止
你可以把代码复制到本地机器上再运行一次,就可以了。
答案 13 :(得分:-1)
在某些版本的Chrome上,您可以:
ctx.drawImage(image1, 0, 0, w, h);