จับ HTML Canvas เป็น gif / jpg / png / pdf หรือไม่


719

เป็นไปได้หรือไม่ที่จะจับภาพหรือพิมพ์สิ่งที่ปรากฏบนผืนผ้าใบ html เป็นรูปภาพหรือ pdf?

ฉันต้องการสร้างภาพทางผ้าใบและสามารถสร้าง png จากภาพนั้นได้


นี่คือวิธีแก้ปัญหาแบบ pythonic: stackoverflow.com/questions/19395649/ …นอกจากตอบstackoverflow.com/a/3514404/529442
Eugene Gr Philippov


ตัวอย่างที่นี่freakyjolly.com/…
รหัส Spy

1
คุณอาจพบว่าคลัง canvas2image มีประโยชน์สำหรับสิ่งนี้: github.com/hongru/canvas2image
noego

คำตอบ:


737

อุ่ย คำตอบเดิมเป็นคำถามเฉพาะสำหรับคำถามที่คล้ายกัน สิ่งนี้ได้รับการแก้ไขแล้ว:

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

ด้วยค่าใน IMG คุณสามารถเขียนเป็นรูปภาพใหม่ได้ดังนี้:

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

15
อีกหนึ่งคำถามฉันจะบันทึกภาพที่ฉันได้จากแท็กนี้ไปยังเซิร์ฟเวอร์ได้อย่างไร มีเดามั้ย
Surya

7
แต่ถ้าฉันใช้ var img = canvas.toDataURL ("image / jpeg"); ฉันได้พื้นหลังเป็นสีดำสนิท วิธีแก้ไขให้ถูกต้อง
gauti

181
เข้ามา. ฉันตอบสิ่งนี้ในปี 2009 คุณคาดหวังอะไร
donohoe

35
@donohoe จริง ๆ แล้วคุณตอบมันในเดือนสิงหาคม 2010 :)
Nick

13
var img = new Image(); img.src = canvas.toDataURL(); document.body.appendChild(img);มันจะใช้หน่วยความจำน้อยมากที่จะทำสิ่งที่ชอบ document.writeรหัสคือการทำให้ URL ข้อมูลพวกเขาทำให้สตริง HTML แล้ววางสำเนาของสตริงที่ใน DOM เบราว์เซอร์แล้วมีการแยกสตริง HTML ที่ใส่สำเนาอื่นในองค์ประกอบภาพแล้วแยกอีกครั้งเพื่อเปิด data data ไปยัง data data จากนั้นในที่สุดมันก็สามารถแสดงภาพได้ สำหรับภาพขนาดหน้าจอที่มีหน่วยความจำ / คัดลอก / แยกวิเคราะห์จำนวนมาก เพียงข้อเสนอแนะ
gman

116

HTML5 ให้ Canvas.toDataURL (mimetype) ซึ่งใช้ใน Opera, Firefox และ Safari 4 เบต้า อย่างไรก็ตามมีข้อ จำกัด ด้านความปลอดภัยจำนวนมาก (ส่วนใหญ่เกี่ยวข้องกับการวาดเนื้อหาจากแหล่งกำเนิดอื่นบนผืนผ้าใบ)

ดังนั้นคุณไม่จำเป็นต้องมีห้องสมุดเพิ่มเติม

เช่น

 <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
ภาพจะถูกบันทึกไว้ที่ไหน ที่ตั้งในท้องถิ่นของฉัน?
chinna_82

5
ภาพจะปรากฏเป็นภาพในเบราว์เซอร์ของคุณ จากนั้นคุณสามารถบันทึกลงดิสก์หรืออะไรก็ได้ ต่อไปนี้เป็น bookmarklet "Canvas2PNG" ทั่วไปที่รวดเร็วและสกปรกซึ่งจะแปลงผืนผ้าใบแรกในหน้าเป็น PNG และแสดงในเบราว์เซอร์ในหน้าต่างใหม่:javascript:void(window.open().location = document.getElementsByTagName("canvas")[0].toDataURL("image/png"))
Kai Carver

หากภาพมีขนาดไม่กี่ MB ให้เตรียมเบราว์เซอร์ของคุณที่ขัดข้อง (ฉันทำเช่นนั้นสักครู่ใน FireFox)
jahu

1
สิ่งนี้สามารถแก้ไขเพื่อแสดงภาพหลายภาพได้อย่างไร
Mentalist

Data URIs มีความยาวสูงสุดดังนั้นจึงมีข้อ จำกัด สูงสุดเกี่ยวกับขนาดของรูปภาพที่คุณสามารถใส่ลงใน URL ข้อมูล
Juha Syrjälä

44

ฉันคิดว่าฉันจะขยายขอบเขตของคำถามนี้เล็กน้อยด้วยเกร็ดความรู้ที่เป็นประโยชน์ในเรื่องนี้

ในการรับผืนผ้าใบเป็นรูปภาพคุณควรทำสิ่งต่อไปนี้:

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 ที่แตกต่างกันตรวจสอบให้แน่ใจว่าได้เปลี่ยนทั้งภาพ / png แต่ไม่ใช่ภาพ / octet-stream เป็นมูลค่าการกล่าวขวัญว่าถ้าคุณใช้ทรัพยากรข้ามโดเมนใด ๆ ในการเรนเดอร์ Canvas ของคุณคุณจะพบข้อผิดพลาดด้านความปลอดภัยเมื่อคุณพยายามใช้เมธอด toDataUrl


1
ด้วยโซลูชันของคุณสำหรับการดาวน์โหลดไฟล์ฉันต้องเลือกซอฟต์แวร์ที่ฉันต้องการใช้มันไม่สะดวกเลย ... มีวิธีการเปลี่ยน mime เป็น png อีกครั้งหรือไม่?
So4ne

1
@ So4ne ฉันไม่คิดอย่างนั้นมันจะต้องเป็นรูปภาพ / octet-stream เพื่อแจ้งให้ผู้ใช้ดาวน์โหลด หากคุณกำจัดเส้นนั้นคุณจะพบหน้าเปลี่ยนเส้นทางไปยังภาพ ฉันชอบที่จะรู้ว่าถ้ามีคนอื่นรู้วิธีที่จะทำอย่างนี้แม้ว่า
meiamsome

2
ใช้ target = "_ blank" บนลิงก์ <a> และ. คลิก () มันควรจะทำงานเช่นกันเพื่อเรียกการดาวน์โหลด (ทดสอบด้วย FF data-urls และ download = "filename" สำหรับข้อความ / csv และ text / plain)
Ax3l

มันเยี่ยมมาก ขอบคุณ! แต่ถ้าฉันต้องการบันทึกลงในเซิร์ฟเวอร์ไม่ใช่ในเครื่อง อินพุตไฟล์ฟอร์มยอมรับ img src เป็นสิ่งที่อัพโหลดได้หรือไม่?
หัวหน้านักเล่นแร่แปรธาตุ

อุ่ย เพิ่งพบคำตอบ ออกจากคำถามก่อนหน้านี้ในกรณีที่คนอื่นดูดีเช่นกันstackoverflow.com/questions/13198131/…
หัวหน้านักเล่นแร่แปรธาตุ

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

ไฟล์ที่บันทึกไว้จะอยู่ในรูปแบบ. svg จะบันทึกเป็น png ได้อย่างไร?
nullpointer

นี่เป็นทางออกที่ดียกเว้นว่ามันอาจไม่ทำงานบน IE ได้รับข้อผิดพลาด "พื้นที่ข้อมูลที่ส่งผ่านไปยังการเรียกของระบบมีขนาดเล็กเกินไป"
Jose Cherian

ใน Chrome มันแจ้งว่า "Network error" ในขณะที่ Firefox มันใช้งานได้ดี (บน Linux)
Ecker00

20

ฉันจะใช้ " wkhtmltopdf " มันใช้งานได้ดีมาก ใช้เครื่องมือ webkit (ใช้ใน Chrome, Safari, ฯลฯ ) และใช้งานง่ายมาก:

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

แค่นั้นแหละ!

ลองมัน


1
ฉันอยู่ในค่าย wkhtmltopdf เช่นกัน เราใช้มันเพื่อการเก็บถาวรและเป็นที่น่าอัศจรรย์
Daniel Szabo

วิธีการใช้ WKHTMLtoPDF ในหน้าที่ต้องการข้อมูลเข้าสู่ระบบ? ฉันใช้ Jsreport เพื่อแปลงเป็น PDF แต่ฉันจับเนื้อหาของฉันด้วย HTML2Canvas ฉันมีปัญหากับการส่ง Canvas เป็นพารามิเตอร์ไปยัง JSreport
khaled Dehia

@khaledDehia ตรวจสอบ: stackoverflow.com/questions/10287386/…
lepe

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

- ส่งออกรูปภาพเป็น 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();

หรือเป็น PNG แบบโปร่งใส

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

9

อีกวิธีที่น่าสนใจคือPhantomJS มันเป็น WebKit ที่ไม่ต้องใช้สคริปต์กับ JavaScript หรือ CoffeeScript

กรณีการใช้งานอย่างหนึ่งคือการจับภาพหน้าจอ: คุณสามารถบันทึกเนื้อหาเว็บโดยทางโปรแกรมรวมถึง SVG และ Canvas และ / หรือสร้างภาพหน้าจอเว็บไซต์ด้วยภาพตัวอย่างขนาดย่อ

จุดเริ่มต้นที่ดีที่สุดคือหน้า wiki ของการดักจับหน้าจอ

นี่เป็นตัวอย่างที่ดีสำหรับนาฬิกาโพลาร์ (จาก 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

2
+1: PhantomJS เป็นระบบที่เรียบง่ายจัดทำเป็นเอกสารและคิดดีเหมาะสำหรับงานนี้ มันช่วยให้มากกว่าการคว้าผืนผ้าใบด้วย - ตัวอย่างเช่นคุณสามารถปรับเปลี่ยนหน้าหรือส่วนของมัน (ผ่าน JS) ก่อนที่จะทำการจับเพื่อให้ดูในแบบที่คุณต้องการ ที่สมบูรณ์แบบ!
johndodo

1
PhantomJs ล้าสมัยแล้ว
Merc

3

หากคุณกำลังใช้ jQuery ซึ่งมีผู้คนจำนวนมากทำคุณควรใช้คำตอบที่ยอมรับเช่น:

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

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

12
โปรดทราบว่าการใช้ jQuery เพียงอย่างเดียวที่นี่คือการเลือกภาพวาด .toDataURLคือ JS พื้นเมือง
j6m8

ฉันมีปัญหาบันทึกบางคนสามารถช่วยฉันดูลิงค์นี้: stackoverflow.com/questions/25131763/ …
Ramesh S

2
โซลูชัน jQuery ของแท้ (100%) มีดังต่อไปนี้: $('<img>').attr('src',$('#mycanvas')[0].toDataURL('image/png')).appendTo($('#element-to-write-to').empty());หนึ่งบรรทัด
Naeel Maqsudov

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

ข้อมูลเพิ่มเติม: https://github.com/MrRio/jsPDF


1

นี่เป็นวิธีอื่นโดยไม่มีเงื่อนไขถึงแม้ว่าฉันจะไม่รู้จริงๆว่ามันเร็วกว่าหรือไม่ แทน toDataURL (ตามคำถามทั้งหมดที่นี่เสนอ) ในกรณีของฉันต้องการป้องกัน dataUrl / base64 เนื่องจากฉันต้องการบัฟเฟอร์หรือมุมมอง Array ดังนั้นวิธีการอื่น ๆ ในการ 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));
}

ข้อดีอีกอย่างของ blobs คือคุณสามารถสร้าง ObjectUrls เพื่อแสดงข้อมูลเป็นไฟล์คล้ายกับสมาชิก 'files' ของ HTMLInputFile ข้อมูลเพิ่มเติม:

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


-1

ใน Chrome บางเวอร์ชันคุณสามารถ:

  1. ใช้ฟังก์ชั่นภาพวาด ctx.drawImage(image1, 0, 0, w, h);
  2. คลิกขวาบนพื้นที่วาด
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.