Como tirar screenshot de uma div com JavaScript?

175

Estou construindo algo chamado "HTML Quiz". É completamente executado em JavaScript e é bem legal.

No final, aparece uma caixa de resultados que diz "Seus resultados:" e mostra quanto tempo eles levaram, qual porcentagem eles obtiveram e quantas perguntas eles obtiveram em 10. Gostaria de ter um botão que diz "Capturar resultados" e, de alguma forma, faça uma captura de tela ou algo do div e, em seguida, apenas mostre a imagem capturada na página onde eles podem clicar com o botão direito do mouse e "Salvar imagem como".

Eu realmente adoraria fazer isso para que eles possam compartilhar seus resultados com outras pessoas. Eu não quero que eles "copiem" os resultados porque eles podem facilmente mudar isso. Se eles mudarem o que diz a imagem, tudo bem.

Alguém sabe uma maneira de fazer isso ou algo semelhante?

Nathan
fonte
2
Dê uma olhada no phantomjs.org . Com ele, você pode gerar uma imagem completa da página HTML no servidor e fornecer ao usuário um link para fazer o download.
31711 Joshua
Bom artigo Conteúdo HTML para converter e baixar imagens codepedia.info/…
Satinder singh
2
npmjs.com/package/dom-to-image
Stack Underflow

Respostas:

99

Não, eu não sei como fazer uma captura de tela de um elemento, mas o que você pode fazer é desenhar os resultados do questionário em um elemento de tela e usar a função HTMLCanvasElementdo objeto toDataURLpara obter um data:URI com o conteúdo da imagem.

Quando o teste terminar, faça o seguinte:

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

Quando o usuário clicar em "Capturar", faça o seguinte:

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

Isso abrirá uma nova guia ou janela com a 'captura de tela', permitindo ao usuário salvá-la. Não há como invocar um tipo de diálogo 'salvar como'; portanto, é o melhor que você pode fazer na minha opinião.

Delan Azabani
fonte
Obrigado!! Não sei nada sobre tela, mas vou aprender. Como eu usaria os efeitos de desenho do Canvas 2D? Você poderia me ajudar com isso? Muito obrigado! :)
Nathan
5
Experimente a documentação do Mozilla, é realmente muito fácil de seguir: developer.mozilla.org/en/canvas_tutorial
Delan Azabani
Em vez de abri-lo em uma nova janela / guia, posso abri-lo em uma caixa de diálogo embutida? (Como o lightbox plugin de FancyBox?)
Nathan
Sim, você pode ter uma caixa de diálogo embutida com uma imgque esteja srcconfigurada para o data:URI. No entanto, isso não é realmente necessário - você pode simplesmente colocar o canvaspróprio na caixa de diálogo embutida; os usuários podem clicar com o botão direito do mouse e salvar o estado atual como uma imagem.
Delan Azabani 31/07
Obrigado :) eu poderia usar a versão da imagem para que eles serão capazes de clique direito e clique em "Salvar imagem como"
Nathan
89

Esta é uma expansão da resposta de @ Dathan , usando html2canvas e FileSaver.js .

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


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

Esse bloco de código aguarda o btnSaveclique no botão com o ID . Quando está, converte a widgetdiv em um elemento de tela e, em seguida, usa a interface saveAs () FileSaver (via FileSaver.js em navegadores que não a suportam nativamente) para salvar a div como uma imagem chamada "Dashboard.png".

Um exemplo deste trabalho está disponível neste violino .

Andy
fonte
7
Eu precisava adicionar referências a: html2canvas, filesaver, blob, canvas-toBlob. Depois disso, funcionou. Obrigado!
sirthomas
1
Impressionante, essa deve ser a resposta aceita. Simples plug and play.
rgshenoy
Você sabe como isso funcionaria com o three.js? Eu tenho uma div em que o renderizador / tela three.js está (renderer.domElement) e depois divs em cima disso. Eu gostaria de tirar uma foto do pai DIV e capturar tudo? Isso é possível? Obrigado!
Rankinstudio 21/02
1
Você pode evitar que a imagem apareça no seu DOM? Não pode simplesmente fazer o download imediatamente?
kulan
1
Isso funcionou muito bem para mim na primeira tentativa e até parece o estilo CSS original etc. Surpreendentemente preciso. Praticamente uma captura de tela virtual com esse nível de precisão. Usei o CDN para as 2 bibliotecas que precisei adicionar: <! - 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

Após horas de pesquisa, finalmente encontrei uma solução para capturar uma captura de tela de um elemento, mesmo que o origin-cleanFLAG esteja definido (para evitar o XSS), por isso é possível capturar, por exemplo, o Google Maps (no meu caso). Eu escrevi uma função universal para obter uma captura de tela. Além disso, você só precisa da biblioteca html2canvas ( https://html2canvas.hertzen.com/ ).

Exemplo:

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

Lembre-se console.log()e alert()não gerará saída se o tamanho da imagem for ótimo.

Função:

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
    });
}
orange01
fonte
função html2canvas onde você definiu
Bharathi
Integrei a biblioteca html2canvas (veja o link no meu post) antes que a função getScreenshotOfElement () seja atingida. Por exemplo, com a tag <script>. Basta baixar o html2canvas e copiá-lo para o seu projeto.
precisa saber é o seguinte
@ orange01 - Ainda não consigo capturar ruas do google map. Isso apenas captura de tela o botão Mais zoom e Zoom, Mapa e texto do satélite. você pode ajudar a capturar o mapa completo. Obrigado
Farhan Sahibole
7

Se você deseja ter a caixa de diálogo "Salvar como", basta passar a imagem para o script php, que adiciona cabeçalhos apropriados

Exemplo de script "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>
shukshin.ivan
fonte
3

Você não pode fazer uma captura de tela: seria um risco de segurança irresponsável permitir que você o faça. No entanto, você pode:

  • Faça coisas do lado do servidor e gere uma imagem
  • Desenhe algo semelhante a um Canvas e o renderize a uma imagem (em um navegador compatível)
  • Use alguma outra biblioteca de desenhos para desenhar diretamente na imagem (lenta, mas funcionaria em qualquer navegador)
bgw
fonte
Obrigado! Eu irei com a tela, pois não conheço nenhuma "biblioteca de desenho" nem sei como fazer isso. Também não sou tão bom em telas, mas vou tentar.
31411 Nathan
2
Desculpe, tenho que comentar isso, pois é do google, mas o WTF "risco de segurança irresponsável" poderia perguntar por que, se você permitir que o javascript faça uma captura de tela ou uma foto em determinados cabos e retorne os dados codificados em BASE64 como uma string, sem problemas de segurança
Barkermn01
@MartinBarker Eu poderia (por exemplo) abrir a página do Facebook de alguém em um iframe e depois capturá-la. Normalmente, um iframe entre sites não faz parte do DOM acessível para impedir o XSS, mas seria muito mais difícil para o fornecedor do navegador pará-lo nesse caso.
bgw
2
@MartinBarker O Javascript pode fornecer os dados a uma fonte maliciosa sem que o usuário perceba, enquanto a "tela de impressão" é controlada pelo usuário, e não um site arbitrário e não confiável.
bgw
2
Que batida. @PiPeep, se uma página da web pode carregar um iFrame do Facebook, já existe isolamento, por que não estender esse isolamento com uma API de captura de tela? por exemplo. Div.ToPNG (), onde o mencionado iFrame é apagado? Não é um risco de segurança irresponsável, simplesmente não existe, e ninguém trabalhou com "potenciais" problemas de privacidade / segurança no desenvolvimento de uma API de captura de tela incorporada. Seria imensamente útil para uma infinidade de casos de uso, como suporte a aplicativos da Web da intranet, por exemplo.
Todd
3
var shot1=imagify($('#widget')[0], (base64) => {
  $('img.screenshot').attr('src', base64);
});

Dê uma olhada no pacote htmlshot e verifique profundamente a seção do lado do cliente :

npm install htmlshot
Abdennour TOUMI
fonte
esse projeto htmlshot ainda é mantido? O link do github do npm ainda não parece existir. Existe outro site para isso?
whatsTheDiff
@whatsTheDiff:: me dê seus requisitos e tenha certeza de que eu o ajudarei. desde que eu sou o autor desta biblioteca.
Abdennour TOUMI
2
Obrigado @ abdennour-toumi. Acabei de instalar o NPM para examinar o código. Parece que ele está usando o html2canvas, que eu tentei e estou tendo problemas no IE, devido aos SVGs e à complexidade da minha página. Parece que esta biblioteca não vai me ajudar nesse sentido.
whatsTheDiff
4
Seu cliente precisa evitar o IE .. diga isso a eles!
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>

cotação

fallen.lu
fonte
1

É simples: você pode usar esse código para capturar a captura de tela de uma área específica, para definir o ID da div em html2canvas. Estou usando aqui 2 div-:

div id = "carro"
div id = "chartContainer"
se você quiser capturar apenas carros, então use car eu estou capturando aqui carro, apenas você pode alterar o chartContainer para capturar o gráfico html2canvas ($ ( '#car') copie e cole este código

<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
fonte
1
Por que o jQuery é incluído duas vezes e por que o jQuery UI está incluído? Honestamente, o jQuery não é necessário aqui, pelo que posso dizer. document.getElementById('car')oudocument.querySelector('#car')
Nathan
Na verdade, estou trabalhando no meu projeto. então eu apenas esqueço de remover a biblioteca e nada.
Rudra Pratap Singh
0

Tanto quanto sei que você não pode fazer isso, posso estar errado. No entanto, eu faria isso com o php, geraria um JPEG usando as funções padrão do php e depois exibia a imagem, não deve ser um trabalho muito difícil, mas depende de quão chamativo o conteúdo do DIV é

Saulius Antanavicius
fonte
O OP nunca sugeriu que ele estivesse usando o processamento no servidor, mas, se estivesse, isso seria bom.
Delan Azabani 31/07
Sim, infelizmente não posso usar coisas do lado do servidor. Provavelmente irei com uma das respostas acima. Mas obrigada! :)
Nathan
-3

Tanto quanto eu sei, não é possível com javascript.

O que você pode fazer para cada resultado cria uma captura de tela, salve-a em algum lugar e aponte o usuário quando clicar em Salvar resultado. (Eu acho que nenhum resultado é apenas 10, então não é grande coisa criar 10 jpegs de imagem)

Seul
fonte
Obrigado por responder :) Você está certo! Eu poderia criar apenas 10 imagens JPG dos resultados e, depois que eles clicarem no botão, a imagem será salva. O único problema é que, quando eles iniciam o questionário, precisam digitar seu nome, e eu o mostro no questionário. No final da caixa de resultados, ele mostra o nome e diz um comentário como "Melhorando" etc. Eu não seria capaz de colocar o nome deles na imagem ou seria?
31411 Nathan