Przechwycić HTML Canvas jako gif / jpg / png / pdf?


719

Czy można uchwycić lub wydrukować to, co jest wyświetlane na kanwie HTML jako obraz lub pdf?

Chciałbym wygenerować obraz przez płótno i móc wygenerować png z tego obrazu.




Przykład tutaj freakyjolly.com/…
Code Spy

1
Przydatna może być biblioteka canvas2image: github.com/hongru/canvas2image
noego

Odpowiedzi:


737

Ups Oryginalna odpowiedź była specyficzna na podobne pytanie. Zostało to poprawione:

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

z wartością w IMG możesz zapisać go jako nowy Obraz w następujący sposób:

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

15
jeszcze jedno pytanie, jak mogę zapisać obraz otrzymany w tym znaczniku na serwerze. Jakieś pomysły??
Surya

7
Ale jeśli użyję var img = canvas.toDataURL ("image / jpeg"); otrzymuję tło jako kompletne czarne. Jak to naprawić
gauti

181
Och przestań. Odpowiedziałem na to w 2009 roku. Czego oczekujesz?
donohoe

35
@donohoe faktycznie odpowiedziałeś na nie w sierpniu 2010 roku :)
nick

13
Zużyłoby dużo mniej pamięci, aby zrobić coś takiego var img = new Image(); img.src = canvas.toDataURL(); document.body.appendChild(img);. document.writeKod czyni URL danych, ich tworzenia ciąg HTML, a następnie oddanie kopię tego napisu w DOM, przeglądarka musi następnie przeanalizować ten ciąg HTML umieścić inną kopię na elemencie obrazu, a następnie analizować je ponownie, aby włączyć adres URL danych do danych obrazu, a następnie w końcu może wyświetlić obraz. Dla obrazu o rozmiarze ekranu, który jest ogromną ilością pamięci / kopiowania / parsowania. Tylko sugestia
gman

116

HTML5 zapewnia Canvas.toDataURL (mimetype), który jest zaimplementowany w Operze, Firefoksie i Safari 4 beta. Istnieje jednak szereg ograniczeń bezpieczeństwa (głównie związanych z rysowaniem treści z innego źródła na płótnie).

Więc nie potrzebujesz dodatkowej biblioteki.

na przykład

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

Teoretycznie powinno to utworzyć, a następnie przejść do obrazu z zielonym kwadratem pośrodku, ale nie testowałem.


2
Gdzie obraz zostanie zapisany. Lokalizacja w mojej okolicy?
chinna_82,

5
obraz zostanie wyświetlony w przeglądarce jako obraz. Następnie możesz zapisać go na dysku lub cokolwiek innego. Oto szybka i brudna ogólna bookmarklet „Canvas2PNG”, który konwertuje pierwsze płótno na stronie do formatu PNG i wyświetla je w przeglądarce w nowym oknie:javascript:void(window.open().location = document.getElementsByTagName("canvas")[0].toDataURL("image/png"))
Kai Carver

Jeśli obraz ma kilka MB, przygotuj się na awarię przeglądarki (zrobiłem to jakiś czas temu w FireFoxie).
jahu

1
Jak można to zmodyfikować, aby renderować wiele obrazów?
Mentalista

Identyfikatory URI danych mają maksymalną długość, więc istnieje górny limit rozmiaru obrazu, który można umieścić w adresie danych.
Juha Syrjälä

44

Pomyślałem, że nieco rozszerzę zakres tego pytania, z kilkoma przydatnymi ciekawostkami na ten temat.

Aby uzyskać płótno jako obraz, wykonaj następujące czynności:

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

Możesz użyć tego do zapisania obrazu na stronie:

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

Gdzie „image / png” to typ MIME (png jest jedynym, który musi być obsługiwany). Jeśli chcesz mieć tablicę obsługiwanych typów, możesz zrobić coś w ten sposób:

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

Musisz uruchomić to tylko raz na stronie - nigdy nie powinno się zmieniać w całym cyklu życia strony.

Jeśli chcesz, aby użytkownik pobierał plik w miarę zapisywania, możesz wykonać następujące czynności:

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;

Jeśli używasz go z różnymi typami mimów, pamiętaj o zmianie obu instancji image / png, ale nie image / stream oktetów. Warto również wspomnieć, że podczas korzystania z zasobów międzydomenowych przy renderowaniu obszaru roboczego podczas próby użycia metody toDataUrl napotkasz błąd bezpieczeństwa.


1
Z twoim rozwiązaniem pobierania pliku, muszę wybrać oprogramowanie, którego chcę użyć, jest to trochę niewygodne ... Czy istnieje sposób na zastąpienie mime jako png po pobraniu?
So4ne,

1
@ So4ne Nie sądzę, to musi być strumień obrazu / oktetu, aby poprosić użytkownika o pobranie. Jeśli pozbędziesz się tej linii, skończy się strona przekierowująca do obrazu. Chciałbym jednak wiedzieć, czy ktoś inny wie, jak to zrobić ładnie.
meiamsome

2
użycie target = "_ blank" na link <a> i .click () powinno również zadziałać, aby uruchomić pobieranie (testowane z adresami danych FF i download = "nazwa pliku" dla text / csv i text / plain)
Ax3l

To jest świetne. Dzięki! Ale co jeśli chcę zapisać na serwerze, a nie lokalnie? Czy dane wejściowe pliku formularza zaakceptują plik img src jako coś do przesłania?
Główny Alchemik

Ups Właśnie znalazłem odpowiedź. Pozostawiając poprzednie pytanie na wypadek, gdyby ktoś również szukał stackoverflow.com/questions/13198131/...
Główny Alchemik

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

Zapisany plik pozostaje w formacie .svg. Jak zapisać go jako png?
nullpointer

To miłe rozwiązanie, z tym wyjątkiem, że może nie działać w IE. Występuje błąd „Obszar danych przekazywany do wywołania systemowego jest zbyt mały”
Jose Cherian

W Chrome jest napisane „Błąd sieci”, podczas gdy w Firefox działa świetnie. (W systemie Linux)
Ecker00

20

Chciałbym użyć „ wkhtmltopdf ”. Po prostu działa świetnie. Wykorzystuje silnik webkit (używany w Chrome, Safari itp.) I jest bardzo łatwy w użyciu:

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

Otóż ​​to!

Spróbuj


1
Też jestem w obozie wkhtmltopdf. Używaliśmy go do archiwizacji i jest NIESAMOWITE.
Daniel Szabo

Jak korzystać z WKHTMLtoPDF na stronie wymagającej danych logowania? Używam Jsreport do konwersji na PDF, ale przechwytuję swoją zawartość za pomocą HTML2Canvas, mam problem z wysłaniem Canvas jako parametru do JSreport
khaled Dehia


15

Oto pomoc, jeśli wykonujesz pobieranie przez serwer (w ten sposób możesz nazwać / przekonwertować / postproces / etc plik):

-Post danych za pomocą toDataURL

-Ustaw nagłówki

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

-Stwórz obraz

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

-eksportuj obraz jako 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();

-lub jako przezroczysty PNG

imagesavealpha($img, true);
imagepng($img);
die($img);

9

Innym ciekawym rozwiązaniem jest PhantomJS . Jest to bezgłowy skrypt WebKit ze skryptami JavaScript lub CoffeeScript.

Jednym z przypadków użycia jest przechwytywanie ekranu: możesz programowo przechwytywać zawartość sieci, w tym SVG i Canvas i / lub tworzyć zrzuty ekranu witryny internetowej z podglądem miniatur.

Najlepszym punktem wejścia jest zrzut ekranu strony wiki.

Oto dobry przykład zegara polarnego (z RaphaelJS):

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

Czy chcesz renderować stronę do pliku PDF?

> phantomjs rasterize.js 'http://en.wikipedia.org/w/index.php?title=Jakarta&printable=yes' jakarta.pdf

2
+1: PhantomJS to prosty, dobrze udokumentowany i przemyślany system, idealny do tego zadania. Pozwala to na znacznie więcej niż tylko chwytanie obszaru roboczego - na przykład możesz zmodyfikować stronę lub jej część (za pomocą JS) przed pobraniem, aby wyglądała dokładnie tak, jak chcesz. Doskonały!
johndodo

1
PhantomJs jest już nieaktualny
Merc

3

Jeśli używasz jQuery, co robi całkiem sporo osób, wówczas zaimplementuj zaakceptowaną odpowiedź w następujący sposób:

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

$("#elememt-to-write-to").html('<img src="'+img+'"/>');

12
Pamiętaj, że jedynym zastosowaniem jQuery jest tutaj wybór obszaru roboczego. .toDataURLjest rodzimym JS.
j6m8

Mam problem z zapisaniem, ktoś może mi pomóc zobaczyć ten link: stackoverflow.com/questions/25131763/…
Ramesh S

2
Czyste (100%) rozwiązanie jQuery wygląda następująco: $('<img>').attr('src',$('#mycanvas')[0].toDataURL('image/png')).appendTo($('#element-to-write-to').empty());Dokładnie jedna linia.
Naeel Maqsudov

1

Możesz użyć jspdf, aby uchwycić płótno na obrazie lub pdf:

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

Więcej informacji: https://github.com/MrRio/jsPDF


1

To jest inny sposób, bez ciągów, chociaż tak naprawdę nie wiem, czy jest szybszy, czy nie. Zamiast toDataURL (jak proponują wszystkie pytania tutaj). W moim przypadku chcę zapobiec dataUrl / base64, ponieważ potrzebuję bufora lub widoku tablicy. Tak więc inną metodą w HTMLCanvasElement jest toBlob. (Funkcja 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));
}

Kolejną zaletą obiektów blob jest to, że można tworzyć ObjectUrls do reprezentowania danych jako pliki, podobnie jak członek „plików” HTMLInputFile. Więcej informacji:

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


-1

W niektórych wersjach Chrome możesz:

  1. Użyj funkcji rysowania obrazu ctx.drawImage(image1, 0, 0, w, h);
  2. Kliknij płótno prawym przyciskiem myszy
Korzystając z naszej strony potwierdzasz, że przeczytałeś(-aś) i rozumiesz nasze zasady używania plików cookie i zasady ochrony prywatności.
Licensed under cc by-sa 3.0 with attribution required.