Jak zrobić zrzut ekranu div z JavaScriptem?

175

Tworzę coś, co nazywa się „quizem HTML”. Jest całkowicie uruchomiony w JavaScript i jest całkiem fajny.

Na końcu wyskakuje okienko wyników z napisem „Twoje wyniki:” i pokazuje, ile czasu zajęło im, jaki procent otrzymali i ile pytań otrzymali bezpośrednio na 10. Chciałbym mieć przycisk z napisem „Przechwyć wyniki” i poproś o wykonanie zrzutu ekranu lub czegoś w rodzaju elementu div, a następnie wyświetl przechwycony obraz na stronie, na którym można kliknąć prawym przyciskiem myszy i „Zapisz obraz jako”.

Naprawdę chciałbym to zrobić, aby mogli podzielić się swoimi wynikami z innymi. Nie chcę, aby „kopiowali” wyniki, ponieważ mogą to łatwo zmienić. Jeśli zmienią to, co jest napisane na obrazku, no cóż.

Czy ktoś zna sposób na zrobienie tego lub coś podobnego?

Nathan
źródło
2
Zajrzyj na phantomjs.org . Używając go, można wygenerować pełny obraz strony HTML na serwerze i dać użytkownikowi link do pobrania.
Joshua,
Ładny artykuł Treść HTML do konwersji obrazu i pobrania codepedia.info/…
Satinder singh
2
npmjs.com/package/dom-to-image
Stack Underflow,

Odpowiedzi:

98

Nie, nie znam sposobu na „zrzut ekranu” elementu, ale co możesz zrobić, to narysować wyniki quizu w elemencie canvas, a następnie użyć funkcji HTMLCanvasElementobiektu, toDataURLaby uzyskać data:identyfikator URI z zawartością obrazu.

Po zakończeniu quizu wykonaj następujące czynności:

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

Gdy użytkownik kliknie „Przechwyć”, wykonaj następujące czynności:

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

Spowoduje to otwarcie nowej karty lub okna ze „zrzutem ekranu”, umożliwiając użytkownikowi jego zapisanie. Nie ma możliwości wywołania okna dialogowego „Zapisz jako”, więc moim zdaniem jest to najlepsze, co możesz zrobić.

Delan Azabani
źródło
Dzięki!! Nie wiem nic o płótnie, ale się tego nauczę. Jak mogę wykorzystać efekty rysowania Canvas 2D? Czy możesz mi w tym pomóc? Dzięki wielkie! :)
Nathan,
5
Wypróbuj dokumentację Mozilli, jest naprawdę łatwa do naśladowania: developer.mozilla.org/en/canvas_tutorial
Delan Azabani
Zamiast otwierać go w nowym oknie / karcie, czy mogę otworzyć go w wbudowanym oknie dialogowym? (Podobnie jak wtyczka Fancybox lightbox?)
Nathan
Tak, można mieć okno dialogowe z inline imgże ma swój srczestaw do data:URI. Jednak nie jest to naprawdę konieczne - możesz po prostu umieścić to canvassamo w wbudowanym oknie dialogowym; użytkownicy mogą kliknąć go prawym przyciskiem myszy i zapisać bieżący stan jako obraz.
Delan Azabani
Dzięki :) Mogę użyć wersji graficznej, więc będą mogli kliknąć prawym przyciskiem myszy i kliknąć „zapisz obraz jako”
Nathan
89

Jest to rozszerzenie @ Datanowi za odpowiedź , używając html2canvas i FileSaver.js .

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


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

Ten blok kodu czeka na kliknięcie przycisku o identyfikatorze btnSave. Gdy tak jest, konwertuje element widgetdiv na element kanwy, a następnie używa interfejsu saveAs () FileSaver (za pośrednictwem FileSaver.js w przeglądarkach, które nie obsługują go natywnie), aby zapisać element div jako obraz o nazwie „Dashboard.png”.

Przykład takiej pracy jest dostępny pod tym skrzypcem .

Andy
źródło
7
Musiałem dodać odniesienia do: html2canvas, filesaver, blob, canvas-toBlob. Potem zadziałało. Dzięki!
sirthomas
1
Świetnie, to powinna być akceptowana odpowiedź. Prosta funkcja plug and play.
rgshenoy
Czy wiesz, jak to by działało z three.js? Mam element div, w którym znajduje się renderer / canvas three.js (renderer.domElement), a następnie elementy div na tym. Chciałbym zrobić migawkę rodzica DIV i uchwycić wszystko? czy to możliwe? Dzięki!
Rankinstudio,
1
Czy możesz uniknąć pojawienia się obrazu w Twoim DOM? Czy nie można go od razu pobrać?
kulan
1
To działało świetnie za pierwszym razem, a nawet wygląda jak oryginalny styl CSS itp. Niezwykle dokładne. Prawie wirtualny zrzut ekranu z tym poziomem dokładności. Użyłem CDN dla 2 bibliotek, które musiałem dodać: <! - html2canvas -> <script src = " cdnjs.cloudflare.com/ajax/libs/html2canvas/0.4.1/… > <! - filesaver - > <script src = " cdn.jsdelivr.net/npm/[email protected]/dist/… >
OG Sean
13

Po godzinach poszukiwań w końcu znalazłem rozwiązanie, aby zrobić zrzut ekranu elementu, nawet jeśli ustawiono origin-cleanFLAG (aby zapobiec XSS), dlatego możesz nawet przechwycić na przykład Google Maps (w moim przypadku). Napisałem uniwersalną funkcję, aby uzyskać zrzut ekranu. Jedyne, czego potrzebujesz, to biblioteka html2canvas ( https://html2canvas.hertzen.com/ ).

Przykład:

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

Należy pamiętać console.log()i alert()nie generuje danych wyjściowych, jeśli rozmiar obrazu jest duży.

Funkcjonować:

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
    });
}
pomarańczowy01
źródło
gdzie zdefiniowałeś funkcję html2canvas
Bharathi,
Zintegrowałem bibliotekę html2canvas (patrz link w moim poście) przed osiągnięciem funkcji getScreenshotOfElement (). Na przykład z tagiem <script> -tag. Po prostu pobierz html2canvas i skopiuj go do swojego projektu.
pomarańczowy 01
@ orange01 - Nadal nie mogę uchwycić pasów ulic na mapie Google. To tylko zrzut ekranu przycisku powiększania i pomniejszania, mapy i tekstu satelitarnego. czy możesz pomóc uchwycić pełną mapę. Dzięki
Farhan Sahibole
7

Jeśli chcesz mieć okno dialogowe „Zapisz jako”, po prostu prześlij obrazek do skryptu php, który doda odpowiednie nagłówki

Przykładowy skrypt „wszystko w jednym” 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>
shukshin.ivan
źródło
3

Nie możesz zrobić zrzutu ekranu: pozwolenie na to byłoby nieodpowiedzialnym zagrożeniem dla bezpieczeństwa. Możesz jednak:

  • Rób rzeczy po stronie serwera i generuj obraz
  • Narysuj coś podobnego do Canvas i wyrenderuj to na obrazie (w przeglądarce, która go obsługuje)
  • Użyj innej biblioteki rysunków, aby rysować bezpośrednio na obrazie (wolno, ale działa w dowolnej przeglądarce)
bgw
źródło
Dzięki! Pójdę z płótnem, ponieważ nie znam żadnych „bibliotek rysunkowych” ani nie wiem, jak to zrobić. Ja też nie jestem dobry w płótnie, ale spróbuję.
Nathan,
2
Przepraszam, muszę to skomentować, ponieważ pochodzi z Google, ale WTF "nieodpowiedzialne zagrożenie bezpieczeństwa" może zapytać dlaczego, jeśli pozwolisz javascriptowi zrobić zrzut ekranu lub strzał w podanych kablach i zwrócisz zakodowane dane BASE64 jako ciąg bez problemu z bezpieczeństwem
Barkermn01
@MartinBarker Mógłbym (na przykład) wyświetlić czyjąś stronę na Facebooku w ramce iframe, a następnie zrobić zrzut ekranu. Zwykle element iframe z różnych witryn nie jest częścią dostępnego DOM, aby zapobiec XSS, ale w takim przypadku producentowi przeglądarki byłoby znacznie trudniej go zatrzymać.
bgw
2
@MartinBarker JavaScript może przekazać dane do złośliwego źródła, nawet nie zauważając tego użytkownika, podczas gdy „print screen” jest kontrolowany przez użytkownika, a nie przez jakąś dowolną niezaufaną witrynę.
bgw
2
Co za pobicie. @PiPeep, Jeśli strona internetowa może załadować iFrame Facebooka, izolacja już istnieje, dlaczego nie rozszerzyć takiej izolacji za pomocą interfejsu API zrzutów ekranu? na przykład. Div.ToPNG (), gdzie wspomniana wcześniej ramka iFrame jest wyłączona? Nie jest to nieodpowiedzialne zagrożenie bezpieczeństwa, po prostu nie ma go wbudowanego i nikt nie rozwiązał „potencjalnych” problemów z prywatnością / bezpieczeństwem podczas opracowywania takiego wbudowanego interfejsu API zrzutów ekranu. Byłoby niezwykle użyteczne w niezliczonych przypadkach użycia, takich jak na przykład obsługa intranetowych aplikacji internetowych.
Todd,
3
var shot1=imagify($('#widget')[0], (base64) => {
  $('img.screenshot').attr('src', base64);
});

Przyjrzyj się pakietowi htmlshot , a następnie dokładnie sprawdź sekcję po stronie klienta :

npm install htmlshot
Abdennour TOUMI
źródło
czy ten projekt htmlshot jest nadal utrzymywany? Link do github z npm wygląda na to, że nie istnieje. Czy jest do tego inna strona internetowa?
whatsTheDiff
@whatsTheDiff:: podaj mi swoje wymagania i bądź pewien, że ci pomogę, ponieważ jestem autorem tej biblioteki.
Abdennour TOUMI
2
Dzięki @ abdennour-toumi. Właśnie wykonałem instalację npm, aby przejrzeć kod. Wygląda na to, że używa html2canvas, które próbowałem i mam problemy z IE oraz ze względu na SVG i złożoność mojej strony. Wygląda więc na to, że ta biblioteka mi w tym nie pomoże.
whatsTheDiff
4
Twój klient musi unikać IE .. powiedz im to!
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>

cytat

fallen.lu
źródło
1

Jest zbyt proste, możesz użyć tego kodu do przechwycenia zrzutu ekranu określonego obszaru, musisz zdefiniować identyfikator div w html2canvas. Używam tutaj 2 div-:

div id = "samochód"
div id = "chartContainer"
jeśli chcesz przechwytywać tylko samochody, użyj samochodu, który tu przechwytuje, tylko samochód możesz zmienić chartContainer, aby przechwycić wykres html2canvas ($ ( '#car') skopiuj i wklej ten kod

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

Rudra Pratap Singh
źródło
1
Dlaczego jQuery jest dołączony dwukrotnie i dlaczego jest dołączony interfejs użytkownika jQuery? Szczerze mówiąc, z tego co wiem, jQuery nie jest tu wcale potrzebne. document.getElementById('car')lubdocument.querySelector('#car')
Nathan
właściwie to pracuję nad swoim projektem. więc po prostu zapomniałem o usunięciu biblioteki i nic.
Rudra Pratap Singh
0

O ile wiem, że nie możesz tego zrobić, mogę się mylić. Jednak zrobiłbym to z php, wygenerowałbym JPEG za pomocą standardowych funkcji php, a następnie wyświetlał obraz, nie powinno to być bardzo trudne, jednak zależy to od tego, jak krzykliwa jest zawartość DIV

Saulius Antanavicius
źródło
OP nigdy nie zasugerował, że używa przetwarzania po stronie serwera, chociaż gdyby tak było, byłoby dobrze.
Delan Azabani
Tak, niestety nie mogę używać rzeczy po stronie serwera. Prawdopodobnie skorzystam z jednej z powyższych odpowiedzi. Ale dzięki! :)
Nathan,
-3

O ile wiem, nie jest to możliwe w przypadku javascript.

Co możesz zrobić dla każdego wyniku, stwórz zrzut ekranu, zapisz go gdzieś i wskaż użytkownika po kliknięciu wyniku zapisu. (Myślę, że żaden wynik to tylko 10, więc utworzenie obrazu wyników w formacie 10 jpeg nie jest wielkim problemem)

Seul
źródło
Dzięki za odpowiedź :) Masz rację! Mogę po prostu utworzyć 10 obrazów JPG wyników, a po kliknięciu przycisku pokaże im obraz do zapisania. Jedynym problemem jest to, że kiedy rozpoczynają quiz, muszą wpisać swoje imię i nazwisko, a ja wyświetlam je w całym quizie, a na końcu w polu wyników wyświetla się ich imię i nazwisko oraz komentarz typu „Getting Better” itd. Nie byłbym w stanie umieścić ich nazwy na obrazku, czy nie?
Nathan,