¿Capturar HTML Canvas como gif / jpg / png / pdf?

719

¿Es posible capturar o imprimir lo que se muestra en un lienzo html como imagen o pdf?

Me gustaría generar una imagen a través del lienzo y poder generar un png desde esa imagen.

Desfile
fuente
Aquí hay una solución pitónica: stackoverflow.com/questions/19395649/… además de responder stackoverflow.com/a/3514404/529442
Eugene Gr. Philippov
Ejemplo aquí freakyjolly.com/…
Code Spy
1
Puede encontrar la biblioteca canvas2image útil para esto: github.com/hongru/canvas2image
noego

Respuestas:

737

Ups La respuesta original era específica de una pregunta similar. Esto ha sido revisado:

var canvas = document.getElementById("mycanvas");
var img    = canvas.toDataURL("image/png");

con el valor en IMG puede escribirlo como una nueva imagen de esta manera:

document.write('<img src="'+img+'"/>');
donohoe
fuente
15
Una pregunta más, ¿cómo puedo guardar la imagen que obtuve en esta etiqueta en el servidor? ¿¿Alguna conjetura??
Surya
77
Pero si uso var img = canvas.toDataURL ("image / jpeg"); Estoy obteniendo el fondo como negro completo. Cómo rectificar eso
gauti
181
Oh vamos. Respondí esto en 2009. ¿Qué esperas?
Donohoe
35
@donohoe en realidad lo respondiste en agosto de 2010 :)
nick
13
Usaría mucha menos memoria para hacer algo así var img = new Image(); img.src = canvas.toDataURL(); document.body.appendChild(img);. El document.writecódigo crea la URL de datos, ellos hacen una cadena HTML, luego colocan una copia de esa cadena en el DOM, el navegador debe analizar esa cadena HTML, colocar otra copia en el elemento de imagen y luego analizarla nuevamente para activar el URL de datos en datos de imagen, luego finalmente puede mostrar la imagen. Para una imagen de tamaño de pantalla que es una gran cantidad de memoria / copia / análisis. Solo una sugerencia
gman
116

HTML5 proporciona Canvas.toDataURL (mimetype) que se implementa en Opera, Firefox y Safari 4 beta. Sin embargo, hay una serie de restricciones de seguridad (principalmente relacionadas con el dibujo de contenido de otro origen en el lienzo).

Por lo tanto, no necesita una biblioteca adicional.

p.ej

 <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>

Teóricamente, esto debería crear y luego navegar a una imagen con un cuadrado verde en el medio, pero no lo he probado.

olliej
fuente
2
Donde se guardará la imagen. ¿Ubicación en mi local?
chinna_82
55
la imagen se mostrará como una imagen en su navegador. Luego puede guardarlo en el disco o lo que sea. Aquí hay un marcador rápido y sucio genérico "Canvas2PNG" que convierte el primer lienzo de la página a PNG y lo muestra en el navegador en una nueva ventana:javascript:void(window.open().location = document.getElementsByTagName("canvas")[0].toDataURL("image/png"))
Kai Carver
Si la imagen tiene un tamaño de unos MB, prepárese para bloquear su navegador (lo hice hace un tiempo en Firefox).
Jahu
1
¿Cómo podría modificarse esto para representar múltiples imágenes?
Mentalista
Los URI de datos tienen una longitud máxima, por lo que hay un límite superior en el tamaño de una imagen que puede colocar en una url de datos.
Juha Syrjälä
44

Pensé en ampliar un poco el alcance de esta pregunta, con algunos datos útiles sobre el tema.

Para obtener el lienzo como una imagen, debe hacer lo siguiente:

var canvas = document.getElementById("mycanvas");
var image = canvas.toDataURL("image/png");

Puede usar esto para escribir la imagen en la página:

document.write('<img src="'+image+'"/>');

Donde "image / png" es un tipo mime (png es el único que debe ser compatible). Si desea una matriz de los tipos admitidos, puede hacer algo en este sentido:

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];
    }
}

Solo necesita ejecutar esto una vez por página; nunca debería cambiar durante el ciclo de vida de una página.

Si desea que el usuario descargue el archivo a medida que se guarda, puede hacer lo siguiente:

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;

Si está usando eso con diferentes tipos de mime, asegúrese de cambiar ambas instancias de image / png, pero no la imagen / octeto-stream. También vale la pena mencionar que si usa cualquier recurso de dominio cruzado para representar su lienzo, encontrará un error de seguridad cuando intente usar el método toDataUrl.

meiamsome
fuente
1
Con su solución para descargar el archivo, debo elegir el software que quiero usar, es un poco incómodo ... ¿Hay alguna forma de reemplazar el mimo como png una vez descargado?
So4ne
1
@ So4ne No lo creo, tiene que ser una secuencia de imagen / octeto para solicitar una descarga al usuario. Si se deshace de esa línea, terminará con la página redirigiendo a la imagen. Sin embargo, me encantaría saber si alguien más conoce una forma de hacerlo bien.
meiamsome
2
usando target = "_ blank" en el enlace <a> y .click () también debería funcionar para activar la descarga (probado con URL de datos FF y download = "filename" para texto / csv y texto / plano)
Ax3l
Esto es genial. ¡Gracias! Pero, ¿qué pasa si quiero guardar en el servidor y no en el local? ¿Una entrada de archivo de formulario aceptará el img src como algo que se puede cargar?
Jefe alquimista
Ups Acabo de encontrar la respuesta. Dejando la pregunta anterior en caso de que alguien más esté mirando también stackoverflow.com/questions/13198131/…
Chief Alchemist
34
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);
}
david.barkhuizen
fuente
El archivo guardado permanece en formato .svg. ¿Cómo guardarlo como png?
nullpointer
Esta es una buena solución, excepto que puede no funcionar en IE. Obteniendo error "El área de datos pasada a una llamada del sistema es demasiado pequeña"
Jose Cherian
En Chrome dice "Error de red", mientras que en Firefox funciona muy bien. (En Linux)
Ecker00
20

Yo usaría " wkhtmltopdf ". Simplemente funciona muy bien. Utiliza el motor de webkit (utilizado en Chrome, Safari, etc.), y es muy fácil de usar:

wkhtmltopdf stackoverflow.com/questions/923885/ this_question.pdf

¡Eso es!

Intentalo

lepe
fuente
1
También estoy en el campamento wkhtmltopdf. Lo hemos estado usando para archivar y es INCREÍBLE.
Daniel Szabo
¿Cómo usar WKHTMLtoPDF en una página que requiere información de inicio de sesión? Estoy usando Jsreport para convertir a PDF pero capturo mi contenido con HTML2Canvas, tengo problemas para enviar el Canvas como parámetro a JSreport
khaled Dehia
@khaledDehia check: stackoverflow.com/questions/10287386/…
lepe
15

Aquí hay algo de ayuda si realiza la descarga a través de un servidor (de esta manera puede nombrar / convertir / postprocesar / etc su archivo):

-Publicar datos usando toDataURL

-Configurar los encabezados

$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");

-crear imagen

$data = $_POST['data'];
$img = imagecreatefromstring(base64_decode(substr($data,strpos($data,',')+1)));

-exportar imagen como 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();

-o como PNG transparente

imagesavealpha($img, true);
imagepng($img);
die($img);
Comunidad
fuente
9

Otra solución interesante es PhantomJS . Es un WebKit sin cabeza programable con JavaScript o CoffeeScript.

Uno de los casos de uso es la captura de pantalla: puede capturar mediante programación contenidos web, incluidos SVG y Canvas y / o Crear capturas de pantalla del sitio web con una vista previa en miniatura.

El mejor punto de entrada es la página wiki de captura de pantalla .

Aquí hay un buen ejemplo para el reloj polar (de RaphaelJS):

>phantomjs rasterize.js http://raphaeljs.com/polar-clock.html clock.png

¿Quieres renderizar una página a un PDF?

> phantomjs rasterize.js 'http://en.wikipedia.org/w/index.php?title=Jakarta&printable=yes' jakarta.pdf
Cybermaxs
fuente
2
+1: PhantomJS es un sistema simple, bien documentado y bien pensado, perfecto para este trabajo. También permite mucho más que solo agarrar un lienzo; por ejemplo, puede modificar la página o parte de ella (a través de JS) antes de agarrar, así que haga que se vea exactamente como lo desea. ¡Perfecto!
johndodo
1
PhantomJs ahora está obsoleto
Merc
3

Si está utilizando jQuery, lo que hace mucha gente, entonces implementaría la respuesta aceptada de la siguiente manera:

var canvas = $("#mycanvas")[0];
var img = canvas.toDataURL("image/png");

$("#elememt-to-write-to").html('<img src="'+img+'"/>');
Pattle
fuente
12
Tenga en cuenta que el único uso de jQuery aquí es la selección del lienzo. .toDataURLes nativo de JS.
j6m8
Tengo un problema de guardado, alguien puede ayudarme a ver este enlace: stackoverflow.com/questions/25131763/…
Ramesh S
2
La solución jQuery pura (100%) es la siguiente: $('<img>').attr('src',$('#mycanvas')[0].toDataURL('image/png')).appendTo($('#element-to-write-to').empty());exactamente una línea.
Naeel Maqsudov
1

Puede usar jspdf para capturar un lienzo en una imagen o pdf como este:

var imgData = canvas.toDataURL('image/png');              
var doc = new jsPDF('p', 'mm');
doc.addImage(imgData, 'PNG', 10, 10);
doc.save('sample-file.pdf');

Más información: https://github.com/MrRio/jsPDF

ferralucho
fuente
1

Esta es la otra manera, sin cadenas, aunque realmente no sé si es más rápido o no. En lugar de toDataURL (como todas las preguntas aquí proponen). En mi caso, quiero evitar dataUrl / base64 ya que necesito un buffer o vista Array. Entonces, el otro método en HTMLCanvasElement es toBlob. (Función 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));
}

Otra ventaja de los blobs es que puede crear ObjectUrls para representar datos como archivos, de forma similar al miembro de 'archivos' de HTMLInputFile. Más información:

https://developer.mozilla.org/es/docs/Web/API/HTMLCanvasElement/toBlob

cancerbero
fuente
-1

En algunas versiones de Chrome, puedes:

  1. Use la función de dibujar imagen ctx.drawImage(image1, 0, 0, w, h);
  2. Haga clic derecho en el lienzo
Peter
fuente