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.