Как сделать скриншот div с JavaScript?

175

Я строю то, что называется "HTML Quiz". Он полностью работает на JavaScript и это довольно круто.

В конце появляется окно результатов с надписью «Ваши результаты», и оно показывает, сколько времени они заняли, какой процент они получили, и сколько вопросов они получили сразу из 10. Я хотел бы иметь кнопку, которая говорит «Захват результатов» и пусть он каким-то образом делает снимок экрана или что-то из div, а затем просто показывает изображение, захваченное на странице, где они могут щелкнуть правой кнопкой мыши и «Сохранить изображение как».

Я действительно хотел бы сделать это, чтобы они могли поделиться своими результатами с другими. Я не хочу, чтобы они «копировали» результаты, потому что они могут легко это изменить. Если они изменят то, что написано на картинке, да ладно.

Кто-нибудь знает способ сделать это или что-то подобное?

Натан
источник
2
Взгляните на phantomjs.org . С его помощью вы можете создать полное изображение HTML-страницы на сервере и дать пользователю ссылку для загрузки.
Джошуа
Хорошая статья HTML-контент для преобразования изображений и загрузки codepedia.info/…
singh
2
npmjs.com/package/dom-to-image
Stack сгущенного

Ответы:

99

Нет, я не знаю, как «скриншотить» элемент, но что вы могли бы сделать, это нарисовать результаты теста в элемент canvas, а затем использовать функцию 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
Делан Азабани,
Вместо того, чтобы открыть его в новом окне / вкладке, могу ли я открыть его во встроенном диалоговом окне? (Как плагин лайтбокса Fancybox?)
Натан,
Да, вы можете иметь встроенный диалог с imgкоторый имеет свой srcнабор к data:URI. Тем не менее, это не является необходимым - вы можете просто поместить canvasсебя в встроенное диалоговое окно; пользователи могут щелкнуть по нему правой кнопкой мыши и сохранить текущее состояние в виде изображения.
Делан Азабани
Спасибо :) Я мог бы использовать версию изображения, чтобы они могли щелкнуть правой кнопкой мыши и щелкнуть «сохранить изображение как»
Натан
89

Это расширение ответа @ Dathan , используя html2canvas и FileSaver.js .

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


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

Этот блок кода ожидает нажатия кнопки с идентификатором btnSave. Когда это так, он преобразует widgetdiv в элемент canvas и затем использует интерфейс FileAsaver saveAs () (через FileSaver.js в браузерах, которые его не поддерживают изначально), чтобы сохранить div в виде изображения с именем «Dashboard.png».

Пример этой работы доступен на этой скрипке .

Энди
источник
7
Мне нужно было добавить ссылки на: html2canvas, filesaver, blob, canvas-toBlob. После этого это сработало. Спасибо!
Сиртомас
1
Круто, это должен быть принятый ответ. Прямой подключи и играй.
ргшеной
Вы знаете, как это будет работать с three.js? У меня есть div, в котором находится файл трехмерного рендеринга / canvas (renderer.domElement), а затем - div. Я хотел бы сделать снимок родительского DIV и захватить все? Это возможно? Спасибо!
Rankinstudio
1
Можете ли вы избежать появления изображений в вашем DOM? Разве это не может просто пойти прямо сейчас?
Кулан
1
Это отлично сработало для меня с первой попытки, и даже выглядит как оригинальный стиль CSS и т. Д. Удивительно точный. Практически виртуальный снимок экрана с таким уровнем точности. Я использовал CDN для двух библиотек, которые мне пришлось добавить: <! - 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/… >
О. Г. Шон,
13

После нескольких часов исследований я наконец нашел решение сделать скриншот элемента, даже если установлен origin-cleanFLAG (для предотвращения XSS), поэтому вы даже можете захватывать, например, Google Maps (в моем случае). Я написал универсальную функцию, чтобы получить скриншот. Кроме того, вам нужна только библиотека 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
    });
}
orange01
источник
где вы определили функцию html2canvas
Бхарати
Я интегрировал библиотеку html2canvas (см. Ссылку в моем посте) до того, как была достигнута функция getScreenshotOfElement (). Например, с тегом <script>. Просто скачайте html2canvas и скопируйте его в свой проект.
orange01
@ orange01 - Я до сих пор не могу запечатлеть уличные дорожки на карте Google. Это только скриншот кнопки увеличения и уменьшения, карты и спутникового текста. Можете ли вы помочь, чтобы захватить полную карту. Спасибо
Фархан Сахиболе
7

Если вы хотите иметь диалог «Сохранить как», просто передайте изображение в скрипт php, который добавляет соответствующие заголовки

Пример сценария "все в одном" 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
источник
3

Вы не можете сделать снимок экрана: было бы безответственным риском для безопасности позволить вам сделать это. Тем не менее, вы можете:

  • Делайте вещи на стороне сервера и генерируйте изображение
  • Нарисуйте что-то похожее на Canvas и визуализируйте это на изображение (в браузере, который поддерживает это)
  • Используйте другую библиотеку для рисования, чтобы рисовать прямо на изображение (медленно, но будет работать в любом браузере)
BGW
источник
Спасибо! Я пойду с холстом, так как я не знаю никаких «библиотек рисования» и не знаю, как это сделать. Я не очень хорош в холсте, но я попробую.
Натан,
2
Извините, я должен это прокомментировать, так как это исходит от Google, но WTF "безответственный риск безопасности" может спросить, почему, если вы позволите javascript сделать снимок экрана или снимок в заданных шнурах и вернуть его закодированные в BASE64 данные в виде строки, нет проблем с безопасностью
Barkermn01
@MartinBarker Я мог бы (например) открыть чью-то страницу Facebook в фрейме и затем сделать снимок экрана. Обычно межсайтовый iframe не является частью доступного DOM для предотвращения XSS, но поставщику браузера будет гораздо сложнее остановить его в этом случае.
BGW
2
@MartinBarker Javascript может передавать данные злонамеренному источнику, даже если пользователь этого не замечает, тогда как «экран печати» контролируется пользователем, а не каким-либо произвольным ненадежным веб-сайтом.
BGW
2
Какой удар @PiPeep, если веб-страница может загрузить iFrame Facebook, уже есть изоляция, почему бы не расширить такую ​​изоляцию с помощью API скриншотов? например. Div.ToPNG (), где вышеупомянутый iFrame выбелен? Это не безответственный риск для безопасности, он просто не встроен, и никто не работал над «потенциальными» проблемами конфиденциальности / безопасности при разработке такого встроенного 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>

цитата

fallen.lu
источник
1

Это просто, вы можете использовать этот код для захвата скриншота определенной области, вы должны определить div id в html2canvas. Я использую здесь 2 div-:

div id = "car"
div id = "chartContainer",
если вы хотите захватывать только автомобили, тогда используйте car. Я собираю здесь только автомобили, вы можете изменить 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')
Натан
на самом деле я работаю над своим проектом. так что я просто забываю удалить библиотеку и ничего.
Рудра Пратап Сингх
0

Насколько я знаю, вы не можете этого сделать, я могу ошибаться. Однако я бы сделал это с php, сгенерировал бы JPEG с использованием стандартных функций php и затем отобразил изображение, это не должно быть очень трудной работой, однако зависит от того, насколько ярким является содержимое DIV.

Саулюс Антанавичюс
источник
ОП никогда не предлагал, чтобы он использовал обработку на стороне сервера, хотя, если бы он был, это было бы хорошо.
Делан Азабани
Да, я не могу использовать серверные вещи, к сожалению. Я, вероятно, пойду с одним из ответов выше. Но спасибо! :)
Натан
-3

Насколько я знаю, это невозможно с javascript.

Что вы можете сделать для каждого результата - создать скриншот, сохранить его где-нибудь и указать пользователю, когда нажмете сохранить результат. (Я думаю, что нет результата только 10, так что нет ничего страшного в создании 10 JPEG изображения результатов)

сеул
источник
Спасибо за ответ :) Вы правы! Я мог бы просто создать 10 JPG-изображений результатов, а затем, как только они нажмут кнопку, будет показано изображение для сохранения. Единственная проблема заключается в том, что, когда они запускают тест, им нужно ввести свое имя, и я отображаю его во время теста, а в конце в окне результатов он показывает их имя и говорит комментарий типа «Становится лучше». и т. д. Я бы не смог вписать их название в изображение или нет?
Натан,