วิธีจับภาพหน้าจอของ div ด้วย JavaScript


175

ฉันกำลังสร้างสิ่งที่เรียกว่า "แบบทดสอบ HTML" มันรันบน JavaScript อย่างสมบูรณ์และมันก็ค่อนข้างเท่ห์

ในตอนท้ายกล่องผลลัพธ์จะปรากฏขึ้นที่ระบุว่า "Your Results:" และมันแสดงให้เห็นว่าพวกเขาใช้เวลานานเท่าไหร่เปอร์เซ็นต์ที่พวกเขาได้รับและจำนวนคำถามที่พวกเขาได้รับทันทีจาก 10 ฉันอยากจะมีปุ่มที่บอกว่า "บันทึกผลลัพธ์" แล้วให้จับภาพหน้าจอหรืออะไรก็ได้ของ div แล้วแสดงภาพที่จับบนหน้าเว็บที่พวกเขาสามารถคลิกขวาและ "บันทึกภาพเป็น"

ฉันชอบที่จะทำสิ่งนี้เพื่อให้พวกเขาสามารถแบ่งปันผลลัพธ์กับผู้อื่นได้ ฉันไม่ต้องการให้พวกเขา "คัดลอก" ผลลัพธ์เพราะพวกเขาสามารถเปลี่ยนแปลงได้อย่างง่ายดาย หากพวกเขาเปลี่ยนสิ่งที่พูดในภาพก็โอเค

ไม่มีใครรู้วิธีการทำเช่นนี้หรือสิ่งที่คล้ายกัน?


2
มีลักษณะที่phantomjs.org การใช้มันคุณสามารถสร้างภาพเต็มของหน้า HTML บนเซิร์ฟเวอร์และให้ลิงก์แก่ผู้ใช้ในการดาวน์โหลด
Joshua

บทความดีเนื้อหา HTML เพื่อแปลงภาพและดาวน์โหลดcodepedia.info/ …
Satinder singh

คำตอบ:


98

ไม่ฉันไม่รู้วิธีการ 'จับภาพ' องค์ประกอบ แต่สิ่งที่คุณทำได้คือวาดผลการทดสอบลงในองค์ประกอบภาพวาดจากนั้นใช้ฟังก์ชันHTMLCanvasElementของวัตถุtoDataURLเพื่อรับdata:URI พร้อมเนื้อหาของภาพ

เมื่อทำแบบทดสอบเสร็จให้ทำดังนี้

var c = document.getElementById('the_canvas_element_id');
var t = c.getContext('2d');
/* then use the canvas 2D drawing functions to add text, etc. for the result */

เมื่อผู้ใช้คลิก "จับภาพ" ให้ทำสิ่งนี้:

window.open('', document.getElementById('the_canvas_element_id').toDataURL());

การดำเนินการนี้จะเปิดแท็บหรือหน้าต่างใหม่ด้วย 'ภาพหน้าจอ' ทำให้ผู้ใช้สามารถบันทึกได้ ไม่มีวิธีที่จะเรียกใช้กล่องโต้ตอบ 'บันทึกเป็น' แปลก ๆ ได้ดังนั้นนี่เป็นวิธีที่ดีที่สุดในความคิดของฉัน


ขอบคุณ !! ฉันไม่รู้อะไรเลยเกี่ยวกับผืนผ้าใบ แต่ฉันจะเรียนรู้ ฉันจะใช้เอฟเฟกต์การวาด Canvas 2D ได้อย่างไร คุณช่วยฉันได้ไหม ขอบคุณมาก! :)
นาธาน

5
ลองใช้เอกสารประกอบของ Mozilla มันค่อนข้างง่ายที่จะติดตาม: developer.mozilla.org/en/canvas_tutorial
Delan Azabani

แทนที่จะเปิดในหน้าต่าง / แท็บใหม่ฉันสามารถเปิดในกล่องโต้ตอบแบบอินไลน์ได้หรือไม่ (เช่นปลั๊กอิน Lightbox Fancybox?)
นาธาน

ใช่คุณสามารถมีกล่องโต้ตอบแบบอินไลน์imgที่มีการsrcตั้งค่าเป็นdata:URI อย่างไรก็ตามนั่นไม่จำเป็นจริงๆ - คุณสามารถใส่canvasตัวเองลงในกล่องโต้ตอบแบบอินไลน์ ผู้ใช้สามารถคลิกขวาและบันทึกสถานะปัจจุบันเป็นภาพ
Delan Azabani

ขอบคุณ :) ฉันอาจใช้รุ่นรูปภาพเพื่อให้พวกเขาสามารถคลิกขวาและคลิก "บันทึกภาพเป็น"
นาธาน

89

นี่คือการขยายตัวของ @ ดาธานของคำตอบโดยใช้html2canvasและFileSaver.js

$(function() { 
    $("#btnSave").click(function() { 
        html2canvas($("#widget"), {
            onrendered: function(canvas) {
                theCanvas = canvas;


                canvas.toBlob(function(blob) {
                    saveAs(blob, "Dashboard.png"); 
                });
            }
        });
    });
});

บล็อครหัสนี้รอให้ปุ่มที่มีรหัสbtnSaveถูกคลิก เมื่อเป็นเช่นนั้นมันจะแปลงwidgetdiv เป็นองค์ประกอบภาพวาดจากนั้นใช้ส่วนต่อประสาน saveAs () FileSaver (ผ่าน FileSaver.js ในเบราว์เซอร์ที่ไม่สนับสนุนแบบดั้งเดิม) เพื่อบันทึก div เป็นภาพชื่อ "Dashboard.png"

ตัวอย่างของการทำงานนี้มีอยู่ที่ซอนี้


7
ฉันต้องการเพิ่มการอ้างอิงถึง: html2canvas, filesaver, blob, canvas-toBlob หลังจากนั้นก็ใช้งานได้ ขอบคุณ!
sirthomas

1
เยี่ยมมากนี่ควรเป็นคำตอบที่ยอมรับได้ เสียบ n เล่นตรงไปตรงมา
rgshenoy

คุณรู้หรือไม่ว่าวิธีนี้ใช้งานได้กับ three.js หรือไม่ ฉันมี div ที่ตัวเรนเดอร์ / ผ้าใบสามอันอยู่ใน (renderer.domElement) แล้วก็ divs อยู่ด้านบน ฉันต้องการถ่ายภาพ DIV ของผู้ปกครองและถ่ายทุกอย่างหรือไม่ เป็นไปได้ไหม ขอบคุณ!
Rankinstudio

1
คุณสามารถหลีกเลี่ยงภาพที่ปรากฏใน DOM ของคุณได้หรือไม่? ไม่สามารถดาวน์โหลดได้ทันทีหรือไม่
kulan

1
สิ่งนี้ใช้งานได้ดีสำหรับฉันในการลองครั้งแรกและแม้กระทั่งดูเหมือนว่าสไตล์ CSS ดั้งเดิม ฯลฯ มีความแม่นยำน่าอัศจรรย์ ถ่ายภาพหน้าจอเสมือนจริงด้วยความแม่นยำระดับนี้ ฉันใช้ CDN สำหรับห้องสมุด 2 แห่งที่ฉันต้องเพิ่ม: <! - html2canvas -> <script src = " cdnjs.cloudflare.com/ajax/libs/html2canvas/0.4.1/… > <! - filesaver - > <script src = " cdn.jsdelivr.net/npm/file-saver@2.0.2/dist/ ...... >
OG Sean

13

หลังจากการวิจัยหลายชั่วโมงในที่สุดฉันก็พบวิธีแก้ปัญหาที่จะจับภาพหน้าจอขององค์ประกอบแม้ว่าorigin-cleanจะตั้งค่า FLAG (เพื่อป้องกัน XSS) นั่นเป็นเหตุผลว่าทำไมคุณถึงสามารถจับภาพตัวอย่างเช่น Google แผนที่ (ในกรณีของฉัน) ฉันเขียนฟังก์ชันสากลเพื่อรับภาพหน้าจอ สิ่งเดียวที่คุณต้องการคือห้องสมุด html2canvas ( https://html2canvas.hertzen.com/ )

ตัวอย่าง:

getScreenshotOfElement($("div#toBeCaptured").get(0), 0, 0, 100, 100, function(data) {
    // in the data variable there is the base64 image
    // exmaple for displaying the image in an <img>
    $("img#captured").attr("src", "data:image/png;base64,"+data);
});

โปรดทราบconsole.log()และalert()จะไม่สร้างผลลัพธ์หากขนาดของภาพยอดเยี่ยม

ฟังก์ชั่น:

function getScreenshotOfElement(element, posX, posY, width, height, callback) {
    html2canvas(element, {
        onrendered: function (canvas) {
            var context = canvas.getContext('2d');
            var imageData = context.getImageData(posX, posY, width, height).data;
            var outputCanvas = document.createElement('canvas');
            var outputContext = outputCanvas.getContext('2d');
            outputCanvas.width = width;
            outputCanvas.height = height;

            var idata = outputContext.createImageData(width, height);
            idata.data.set(imageData);
            outputContext.putImageData(idata, 0, 0);
            callback(outputCanvas.toDataURL().replace("data:image/png;base64,", ""));
        },
        width: width,
        height: height,
        useCORS: true,
        taintTest: false,
        allowTaint: false
    });
}

ที่คุณได้กำหนดฟังก์ชัน html2canvas ไว้
Bharathi

ฉันรวมไลบรารี html2canvas (ดูลิงก์ในโพสต์ของฉัน) ก่อนที่จะถึงฟังก์ชั่น ตัวอย่างเช่นกับ <script> -tag เพียงดาวน์โหลด html2canvas และคัดลอกไปยังโครงการของคุณ
orange01

@ orange01 - ฉันยังคงไม่สามารถจับภาพถนนในแผนที่ google ได้ ภาพหน้าจอนี้เพียงปุ่มซูมเข้าและออกแผนที่และข้อความดาวเทียม คุณช่วยกรุณาจับแผนที่เต็มได้ไหม ขอบคุณ
Farhan Sahibole

7

หากคุณต้องการให้กล่องโต้ตอบ "บันทึกเป็น" เพียงส่งภาพไปยังสคริปต์ PHP ซึ่งเพิ่มส่วนหัวที่เหมาะสม

ตัวอย่างสคริปต์ "all-in-one" script.php

<?php if(isset($_GET['image'])):
    $image = $_GET['image'];

    if(preg_match('#^data:image/(.*);base64,(.*)$#s', $image, $match)){
        $base64 = $match[2];
        $imageBody = base64_decode($base64);
        $imageFormat = $match[1];

        header('Content-type: application/octet-stream');
        header("Pragma: public");
        header("Expires: 0");
        header("Cache-Control: must-revalidate, post-check=0, pre-check=0");
        header("Cache-Control: private", false); // required for certain browsers
        header("Content-Disposition: attachment; filename=\"file.".$imageFormat."\";" ); //png is default for toDataURL
        header("Content-Transfer-Encoding: binary");
        header("Content-Length: ".strlen($imageBody));
        echo $imageBody;
    }
    exit();
endif;?>

<script type='text/javascript' src='http://ajax.googleapis.com/ajax/libs/jquery/1.7.2/jquery.min.js?ver=1.7.2'></script>
<canvas id="canvas" width="300" height="150"></canvas>
<button id="btn">Save</button>
<script>
    $(document).ready(function(){
        var canvas = document.getElementById('canvas');
        var oCtx = canvas.getContext("2d");
        oCtx.beginPath();
        oCtx.moveTo(0,0);
        oCtx.lineTo(300,150);
        oCtx.stroke();

        $('#btn').on('click', function(){
            // opens dialog but location doesnt change due to SaveAs Dialog
            document.location.href = '/script.php?image=' + canvas.toDataURL();
        });
    });
</script>

3

คุณไม่สามารถถ่ายภาพหน้าจอ: เป็นความเสี่ยงด้านความปลอดภัยที่ไม่รับผิดชอบเพื่อให้คุณทำเช่นนั้น อย่างไรก็ตามคุณสามารถ:

  • ทำสิ่งที่ฝั่งเซิร์ฟเวอร์และสร้างภาพ
  • วาดสิ่งที่คล้ายกับ Canvas และแสดงภาพนั้น (ในเบราว์เซอร์ที่สนับสนุน)
  • ใช้ไลบรารีรูปวาดอื่นเพื่อวาดโดยตรงไปยังรูปภาพ (ช้า แต่ใช้ได้กับเบราว์เซอร์ใดก็ได้)

ขอบคุณ! ฉันจะไปกับผืนผ้าใบเพราะฉันไม่รู้ "ห้องสมุดวาดรูป" และฉันไม่รู้จะทำยังไง ฉันไม่เก่งเรื่องผ้าใบ แต่ฉันจะลอง
นาธาน

2
ขออภัยฉันต้องแสดงความคิดเห็นนี้มาจาก Google แต่ WTF "ความเสี่ยงต่อความปลอดภัยไม่รับผิดชอบ" ฉันอาจถามว่าทำไมถ้าคุณปล่อยให้จาวาสคริปต์ใช้ภาพหน้าจอหรือภาพภายในสายที่กำหนดและส่งคืนข้อมูลที่เข้ารหัส BASE64 ของมัน
Barkermn01

@MartinBarker ฉันสามารถยกตัวอย่างหน้า Facebook ของใครบางคนใน iframe แล้วจับภาพหน้าจอ โดยปกติ iframe ข้ามไซต์ไม่ได้เป็นส่วนหนึ่งของ DOM ที่เข้าถึงได้เพื่อป้องกัน XSS แต่มันจะยากกว่ามากสำหรับผู้ขายเบราว์เซอร์ที่จะหยุดมันในกรณีเช่นนี้
bgw

2
@MartinBarker จาวาสคริปต์สามารถให้ข้อมูลกับแหล่งที่เป็นอันตรายโดยที่ผู้ใช้ไม่สังเกตเห็นในขณะที่ "หน้าจอการพิมพ์" ถูกควบคุมโดยผู้ใช้และไม่ใช่บางเว็บไซต์ที่ไม่น่าไว้วางใจ
bgw

2
สิ่งที่เต้น @PiPeep หากหน้าเว็บสามารถโหลด iFrame ของ Facebook ได้มีการแยกกันอยู่แล้วทำไมไม่ขยายการแยกดังกล่าวด้วยภาพหน้าจอ API เช่น. Div.ToPNG () ซึ่ง iFrame ดังกล่าวอยู่ที่ไหน whited out? มันไม่ใช่ความเสี่ยงด้านความปลอดภัยที่ขาดความรับผิดชอบ แต่ก็ไม่มีในตัวและไม่มีใครทำงานผ่านปัญหาความเป็นส่วนตัว / ความปลอดภัย "ที่อาจเกิดขึ้น" ในการพัฒนา API หน้าจอในตัว มันจะมีประโยชน์อย่างมากสำหรับกรณีการใช้งานมากมายเช่นการสนับสนุนเว็บแอปพลิเคชันอินทราเน็ต
ทอดด์

3
var shot1=imagify($('#widget')[0], (base64) => {
  $('img.screenshot').attr('src', base64);
});

ดูแพ็คเกจhtmlshotจากนั้นตรวจสอบส่วนของฝั่งไคลเอ็นต์อย่างละเอียด:

npm install htmlshot

โครงการ htmlshot นี้ยังคงอยู่หรือไม่ ลิงก์ github จาก npm ดูเหมือนจะยังไม่มีอยู่ มีเว็บไซต์อื่นให้หรือไม่
whatsTheDiff

@whatsTheDiff:: ให้ความต้องการของคุณและให้แน่ใจว่าฉันจะช่วยคุณได้ตั้งแต่ฉันเป็นผู้เขียนของห้องสมุดนี้
Abdennour TOUMI

2
ขอบคุณ @ abdennour-toumi ฉันเพิ่งติดตั้ง npm เพื่อดูรหัส ดูเหมือนว่าจะใช้ html2canvas ซึ่งฉันพยายามแล้วและมีปัญหากับใน IE และเนื่องจาก SVG และความซับซ้อนของหน้าเว็บของฉัน ดังนั้นดูเหมือนว่าห้องสมุดนี้จะไม่ช่วยฉันในเรื่องที่เกี่ยวกับ
whatsTheDiff

4
ลูกค้าของคุณต้องหลีกเลี่ยง IE .. บอกพวกเขาว่า!
Abdennour TOUMI

1
<script src="/assets/backend/js/html2canvas.min.js"></script>


<script>
    $("#download").on('click', function(){
        html2canvas($("#printform"), {
            onrendered: function (canvas) {
                var url = canvas.toDataURL();

                var triggerDownload = $("<a>").attr("href", url).attr("download", getNowFormatDate()+"电子签名详细信息.jpeg").appendTo("body");
                triggerDownload[0].click();
                triggerDownload.remove();
            }
        });
    })
</script>

คำอ้างอิง


1

มันง่ายมากที่คุณสามารถใช้รหัสนี้เพื่อจับภาพหน้าจอของพื้นที่เฉพาะที่คุณต้องกำหนด div id ใน html2canvas ฉันใช้ที่นี่ 2 div-:

div id = "รถยนต์"
div id = "chartContainer"
หากคุณต้องการจับภาพรถยนต์เท่านั้นจากนั้นใช้รถยนต์ฉันกำลังจับภาพรถที่นี่เท่านั้นคุณสามารถเปลี่ยนchartContainerเพื่อจับกราฟ html2canvas ($ ( '#car') คัดลอกและวางรหัสนี้

<html>
    <head>


<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<script src="https://code.jquery.com/ui/1.12.1/jquery-ui.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/html2canvas/0.4.1/html2canvas.min.js"></script>
<script src="https://code.jquery.com/jquery-1.12.4.min.js" integrity="sha256-ZosEbRLbNQzLpnKIkEdrPv7lOy9C27hHQ+Xp8a4MxAQ=" crossorigin="anonymous"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/jspdf/1.3.5/jspdf.min.js"></script>
<meta name="viewport" content="width=device-width, initial-scale=1">
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/4.7.0/css/font-awesome.min.css">
<link rel="stylesheet" href="https://use.fontawesome.com/releases/v5.2.0/css/all.css" integrity="sha384-hWVjflwFxL6sNzntih27bfxkr27PmbbK/iSvJ+a4+0owXq79v+lsFkW54bOGbiDQ" crossorigin="anonymous">
<script>
    window.onload = function () {
    
    var chart = new CanvasJS.Chart("chartContainer", {
        animationEnabled: true,
        theme: "light2",
        title:{
            text: "Simple Line Chart"
        },
        axisY:{
            includeZero: false
        },
        data: [{        
            type: "line",       
            dataPoints: [
                { y: 450 },
                { y: 414},
                { y: 520, indexLabel: "highest",markerColor: "red", markerType: "triangle" },
                { y: 460 },
                { y: 450 },
                { y: 500 },
                { y: 480 },
                { y: 480 },
                { y: 410 , indexLabel: "lowest",markerColor: "DarkSlateGrey", markerType: "cross" },
                { y: 500 },
                { y: 480 },
                { y: 510 }

            ]
        }]
    });
    chart.render();
    
    }
</script>
</head>

<body bgcolor="black">
<div id="wholebody">  
<a href="javascript:genScreenshotgraph()"><button style="background:aqua; cursor:pointer">Get Screenshot of Cars onl </button> </a>

<div id="car" align="center">
    <i class="fa fa-car" style="font-size:70px;color:red;"></i>
    <i class="fa fa-car" style="font-size:60px;color:red;"></i>
    <i class="fa fa-car" style="font-size:50px;color:red;"></i>
    <i class="fa fa-car" style="font-size:20px;color:red;"></i>
    <i class="fa fa-car" style="font-size:50px;color:red;"></i>
    <i class="fa fa-car" style="font-size:60px;color:red;"></i>
    <i class="fa fa-car" style="font-size:70px;color:red;"></i>
</div>
<br>
<div id="chartContainer" style="height: 370px; width: 100%;"></div>
<script src="https://canvasjs.com/assets/script/canvasjs.min.js"></script>

<div id="box1">
</div>
</div>>
</body>

<script>

function genScreenshotgraph() 
{
    html2canvas($('#car'), {
        
      onrendered: function(canvas) {

        var imgData = canvas.toDataURL("image/jpeg");
        var pdf = new jsPDF();
        pdf.addImage(imgData, 'JPEG', 0, 0, -180, -180);
        pdf.save("download.pdf");
        
      
      
      }
     });

}

</script>

</html>


1
เหตุใด jQuery จึงรวมสองครั้งและทำไม jQuery UI ถึงรวมอยู่ด้วย สุจริต jQuery ไม่จำเป็นที่นี่จากสิ่งที่ฉันสามารถบอกได้ document.getElementById('car')หรือdocument.querySelector('#car')
นาธาน

ที่จริงฉันกำลังทำงานในโครงการของฉัน ดังนั้นฉันแค่ลืมที่จะลบห้องสมุดและไม่มีอะไร
Rudra Pratap Singh เมื่อ

0

เท่าที่ฉันรู้ว่าคุณไม่สามารถทำเช่นนั้นฉันอาจจะผิด อย่างไรก็ตามฉันจะทำเช่นนี้กับ php สร้าง JPEG โดยใช้ฟังก์ชั่นมาตรฐาน php แล้วแสดงภาพไม่ควรเป็นงานที่หนักมาก แต่ขึ้นอยู่กับว่าเนื้อหาของ DIV เป็นอย่างไร


OP ไม่เคยแนะนำว่าเขากำลังใช้การประมวลผลฝั่งเซิร์ฟเวอร์ถึงแม้ว่าเขาจะเป็นแบบนี้ก็ตาม
Delan Azabani

ใช่ฉันไม่สามารถใช้สิ่งที่ฝั่งเซิร์ฟเวอร์โชคไม่ดี ฉันอาจจะไปกับหนึ่งในคำตอบข้างต้น แต่ขอบคุณ! :)
นาธาน

-3

เท่าที่ฉันรู้ว่ามันเป็นไปไม่ได้กับจาวาสคริปต์

คุณสามารถทำอะไรได้บ้างเพื่อสร้างภาพหน้าจอบันทึกที่ใดที่หนึ่งและชี้ผู้ใช้เมื่อคลิกที่ผลลัพธ์การบันทึก (ฉันเดาว่าไม่มีผลลัพธ์เพียง 10 เท่านั้นดังนั้นจึงไม่ใช่เรื่องใหญ่ที่จะสร้างภาพ 10 jpeg ของผลการค้นหา)


ขอบคุณที่ตอบคำถาม :) คุณพูดถูก! ฉันสามารถสร้างภาพ JPG ได้ 10 ภาพจากนั้นเมื่อพวกเขาคลิกปุ่มมันจะแสดงภาพเพื่อให้พวกเขาบันทึก ปัญหาเดียวก็คือเมื่อพวกเขาเริ่มทำแบบทดสอบพวกเขาจะต้องใส่ชื่อของพวกเขาและฉันแสดงมันออกมาแบบทดสอบและท้ายที่สุดในกล่องผลลัพธ์มันจะแสดงชื่อของพวกเขาและมันแสดงความคิดเห็นเช่น "ดีขึ้น" เป็นต้นฉันจะไม่สามารถใส่ชื่อของพวกเขาในภาพหรือฉันจะ?
นาธาน
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.