Comment faire une capture d'écran d'un div avec JavaScript?

175

Je suis en train de créer quelque chose appelé "HTML Quiz". Il est complètement exécuté sur JavaScript et c'est plutôt cool.

À la fin, une boîte de résultats apparaît qui dit "Vos résultats:" et elle montre combien de temps ils ont pris, quel pourcentage ils ont obtenu et combien de questions ils ont obtenu directement sur 10. J'aimerais avoir un bouton qui dit "Capturez les résultats" et faites-le en quelque sorte prendre une capture d'écran ou quelque chose du div, puis montrez simplement l'image capturée sur la page où ils peuvent faire un clic droit et "Enregistrer l'image sous".

J'adorerais vraiment faire cela pour qu'ils puissent partager leurs résultats avec les autres. Je ne veux pas qu'ils «copient» les résultats parce qu'ils peuvent facilement changer cela. S'ils changent ce qu'il dit dans l'image, eh bien.

Quelqu'un connaît-il un moyen de faire cela ou quelque chose de similaire?

Nathan
la source
2
Jetez un œil à phantomjs.org . En l'utilisant, vous pouvez générer une image complète de la page HTML côté serveur et donner à l'utilisateur un lien à télécharger.
Joshua
Bel article contenu HTML en image convertir et télécharger codepedia.info/…
Satinder singh

Réponses:

99

Non, je ne connais pas un moyen de `` capturer '' un élément, mais ce que vous pourriez faire, c'est dessiner les résultats du quiz dans un élément de canevas, puis utiliser la fonction de l' HTMLCanvasElementobjet toDataURLpour obtenir un data:URI avec le contenu de l'image.

Lorsque le quiz est terminé, procédez comme suit:

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

Lorsque l'utilisateur clique sur "Capturer", procédez comme suit:

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

Cela ouvrira un nouvel onglet ou une nouvelle fenêtre avec la «capture d'écran», permettant à l'utilisateur de l'enregistrer. Il n'y a aucun moyen d'invoquer une sorte de dialogue «enregistrer sous», c'est donc le mieux que vous puissiez faire à mon avis.

Delan Azabani
la source
Merci!! Je ne connais rien à la toile, mais je vais l'apprendre. Comment utiliser les effets de dessin Canvas 2D? Pouvez-vous m'aider avec ça? Merci beaucoup! :)
Nathan
5
Essayez la documentation de Mozilla, c'est vraiment assez facile à suivre: developer.mozilla.org/en/canvas_tutorial
Delan Azabani
Au lieu de l'ouvrir dans une nouvelle fenêtre / onglet, puis-je l'ouvrir dans une boîte de dialogue en ligne? (Comme le plugin lightbox Fancybox?)
Nathan
Oui, vous pouvez avoir une boîte de dialogue en ligne avec un imgqui a son srcensemble à l' data:URI. Cependant, ce n'est pas vraiment nécessaire - vous pouvez simplement mettre le canvaslui - même dans la boîte de dialogue en ligne; les utilisateurs peuvent cliquer dessus avec le bouton droit de la souris et enregistrer l'état actuel sous forme d'image.
Delan Azabani du
Merci :) Je pourrais utiliser la version image afin qu'ils puissent faire un clic droit et cliquer sur "enregistrer l'image sous"
Nathan
89

Il s'agit d'une extension de la réponse de @ Dathan , utilisant html2canvas et FileSaver.js .

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


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

Ce bloc de code attend que le bouton avec l'identifiant btnSavesoit cliqué. Quand c'est le cas, il convertit le widgetdiv en élément de canevas puis utilise l'interface SaveAs () FileSaver (via FileSaver.js dans les navigateurs qui ne le prennent pas en charge nativement) pour enregistrer le div sous une image nommée "Dashboard.png".

Un exemple de ce fonctionnement est disponible sur ce violon .

Andy
la source
7
J'avais besoin d'ajouter des références à: html2canvas, filesaver, blob, canvas-toBlob. Après cela, cela a fonctionné. Merci!
sirthomas
1
Génial, cela devrait être la réponse acceptée. Plug n play simple.
rgshenoy
Savez-vous comment cela fonctionnerait avec three.js? J'ai un div dans lequel se trouve le moteur de rendu / canevas three.js (renderer.domElement), puis des divs en plus de cela. Je voudrais prendre un instantané de la DIV parent et tout capturer? Est-ce possible? Merci!
Rankinstudio
1
Pouvez-vous éviter que l'image n'apparaisse dans votre DOM? Ne peut-il pas simplement télécharger tout de suite?
kulan le
1
Cela a très bien fonctionné pour moi du premier coup, et ressemble même au style CSS original, etc. Étonnamment précis. À peu près une capture d'écran virtuelle avec ce niveau de précision. J'ai utilisé CDN pour les 2 bibliothèques que j'ai dû ajouter: <! - html2canvas -> <script src = " cdnjs.cloudflare.com/ajax/libs/html2canvas/0.4.1/… > <! - filesaver - > <script src = " cdn.jsdelivr.net/npm/[email protected]/dist/… >
JO Sean
13

Après des heures de recherche, j'ai finalement trouvé une solution pour prendre une capture d'écran d'un élément, même si le origin-cleanFLAG est défini (pour éviter XSS), c'est pourquoi vous pouvez même capturer par exemple Google Maps (dans mon cas). J'ai écrit une fonction universelle pour obtenir une capture d'écran. La seule chose dont vous avez besoin en plus est la bibliothèque html2canvas ( https://html2canvas.hertzen.com/ ).

Exemple:

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

Gardez à l'esprit console.log()et alert()ne générera pas de sortie si la taille de l'image est grande.

Fonction:

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
la source
où vous avez défini la fonction html2canvas
Bharathi
J'ai intégré la bibliothèque html2canvas (voir lien dans mon article) avant que la fonction getScreenshotOfElement () ne soit atteinte. Par exemple avec le <script> -tag. Téléchargez simplement html2canvas et copiez-le dans votre projet.
orange01
@ orange01 - Je ne parviens toujours pas à capturer les voies de rue de Google Map. Ceci ne capture que le bouton de zoom avant et arrière, la carte et le texte satellite. pouvez-vous s'il vous plaît aider à capturer la carte complète. Merci
Farhan Sahibole
7

Si vous souhaitez avoir la boîte de dialogue "Enregistrer sous", passez simplement l'image dans le script php, qui ajoute les en-têtes appropriés

Exemple de script "tout-en-un" 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
la source
3

Vous ne pouvez pas prendre de capture d'écran: ce serait un risque de sécurité irresponsable de vous laisser faire. Cependant, vous pouvez:

  • Faire les choses côté serveur et générer une image
  • Dessinez quelque chose de similaire à un canevas et rendez-le dans une image (dans un navigateur qui le prend en charge)
  • Utilisez une autre bibliothèque de dessins pour dessiner directement sur l'image (lent, mais fonctionnerait sur n'importe quel navigateur)
bgw
la source
Merci! J'irai avec le canevas, car je ne connais aucune "bibliothèque de dessin" et je ne sais pas comment faire cela. Je ne suis pas très doué pour la toile non plus, mais je vais essayer.
Nathan
2
Désolé, je dois commenter cela car il vient de Google mais WTF "risque de sécurité irresponsable" pourrais-je demander pourquoi, si vous laissez javascript prendre une capture d'écran ou une photo dans des cordons donnés et renvoyer les données encodées BASE64 sous forme de chaîne aucun problème de sécurité
Barkermn01
@MartinBarker Je pourrais (par exemple) afficher la page Facebook de quelqu'un dans une iframe, puis la capturer. Normalement, une iframe intersite ne fait pas partie du DOM accessible pour empêcher XSS, mais il serait beaucoup plus difficile pour le fournisseur de navigateur de l'arrêter dans ce cas.
bgw
2
@MartinBarker Le Javascript pourrait donner les données à une source malveillante sans même que l'utilisateur s'en aperçoive, alors que "l'écran d'impression" est contrôlé par l'utilisateur, et non par un site Web arbitraire non fiable.
bgw
2
Quel battement. @PiPeep, Si une page Web peut charger un iFrame de Facebook, il y a déjà une isolation, pourquoi ne pas étendre cette isolation avec une API de capture d'écran? par exemple. Div.ToPNG (), où l'iFrame susmentionné est blanchi? Ce n'est pas un risque de sécurité irresponsable, il n'est tout simplement pas intégré et personne n'a résolu des problèmes de confidentialité / sécurité «potentiels» en développant une telle API de capture d'écran intégrée. Ce serait extrêmement utile pour une myriade de cas d'utilisation, tels que la prise en charge d'applications Web intranet par exemple.
Todd
3
var shot1=imagify($('#widget')[0], (base64) => {
  $('img.screenshot').attr('src', base64);
});

Jetez un œil au package htmlshot , puis vérifiez en profondeur la section côté client :

npm install htmlshot
Abdennour TOUMI
la source
ce projet htmlshot est-il toujours maintenu? Le lien github de npm ne semble pas toujours exister. Existe-t-il un autre site Web pour cela?
whatsTheDiff
@whatsTheDiff:: donnez-moi vos exigences, et assurez-vous que je vous aiderai puisque je suis l'auteur de cette bibliothèque.
Abdennour TOUMI
2
Merci @ abdennour-toumi. Je viens de faire une installation npm pour parcourir le code. On dirait qu'il utilise html2canvas que j'ai essayé et que j'ai des problèmes avec IE et en raison des SVG et de la complexité de ma page. Il semble donc que cette bibliothèque ne m'aidera pas à cet égard.
whatsTheDiff
4
Votre client doit éviter IE .. dites-leur ça!
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>

devis

fall.lu
la source
1

C'est simple, vous pouvez utiliser ce code pour capturer la capture d'écran d'une zone particulière que vous devez définir l'id div dans html2canvas. J'utilise ici 2 div-:

div id = "car"
div id = "chartContainer"
si vous voulez capturer uniquement des voitures puis utilisez la voiture Je capture ici la voiture seulement vous pouvez changer chartContainer pour capturer le graphique html2canvas ($ ( '#car') copiez et collez ce code

<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
la source
1
Pourquoi jQuery est-il inclus deux fois et pourquoi jQuery UI est-il inclus? Honnêtement, jQuery n'est pas du tout nécessaire ici d'après ce que je peux dire. document.getElementById('car')oudocument.querySelector('#car')
Nathan
en fait je travaille sur mon projet. donc j'oublie juste de supprimer la bibliothèque et rien.
Rudra Pratap Singh
0

Autant que je sache que vous ne pouvez pas faire ça, je me trompe peut-être. Cependant, je ferais cela avec php, générer un JPEG en utilisant les fonctions standard de php, puis afficher l'image, ne devrait pas être un travail très difficile, mais dépend de la façon dont le contenu du DIV est flashy

Saulius Antanavicius
la source
L'OP n'a jamais suggéré qu'il utilise un traitement côté serveur, mais s'il l'était, ce serait bien.
Delan Azabani
Ouais, je ne peux malheureusement pas utiliser de trucs côté serveur. J'irai probablement avec l'une des réponses ci-dessus. Mais merci! :)
Nathan
-3

Autant que je sache, ce n'est pas possible avec javascript.

Ce que vous pouvez faire pour chaque résultat, créez une capture d'écran, enregistrez-la quelque part et pointez l'utilisateur lorsque vous cliquez sur Enregistrer le résultat. (Je suppose que le résultat n'est que de 10, donc ce n'est pas un gros problème de créer 10 images jpeg de résultats)

Séoul
la source
Merci d'avoir répondu :) Vous avez raison! Je pourrais simplement créer 10 images JPG des résultats, puis une fois qu'ils cliquent sur le bouton, ils afficheront l'image à enregistrer. Le seul problème est que lorsqu'ils commencent le quiz, ils doivent entrer leur nom, et je l'affiche tout au long du quiz, et à la fin dans la zone de résultats, il montre leur nom et il dit un commentaire comme "S'améliorer" etc. Je ne pourrais pas en mettre le nom dans l'image ou le ferais-je?
Nathan du