Использование HTML5 / Canvas / JavaScript для создания снимков экрана в браузере

876

Google "Сообщить об ошибке" или "Инструмент обратной связи" позволяет вам выбрать область вашего окна браузера, чтобы создать скриншот, который отправляется с отзывами об ошибке.

Изображение 94 Снимок экрана Джейсона Малого, размещенный в повторяющемся вопросе.Суб >

Как они это делают? API обратной связи Google JavaScript загружается из здесь и их обзор модуля обратной связи продемонстрирует возможность скриншотов.

  • 2
    Эллиот Спрен написал в твиттере несколько дней назад:> @CatChen Этот пост-поток не является точным. Скриншот Google Feedback сделан полностью на стороне клиента. :)
  • 0
    Это кажется логичным, поскольку они хотят точно понять, как браузер пользователя отображает страницу, а не как он будет отображать ее на стороне сервера, используя свой движок. Если вы отправите только DOM текущей страницы на сервер, он пропустит любые несоответствия в том, как браузер отображает HTML. Это не означает, что ответ Чена неправильный при съемке скриншотов, просто похоже, что Google делает это по-другому.
Показать ещё 5 комментариев
Теги:
canvas
screenshot

3 ответа

1108
Лучший ответ

JavaScript может читать DOM и отображать довольно точное представление об этом с помощью canvas. Я работал над сценарием, который преобразует HTML в изображение холста. Решил сегодня внедрить его в отправку отзывов, как вы описали.

Сценарий позволяет создавать формы обратной связи, которые включают в себя скриншот, созданный в браузере клиента, вместе с формой. Снимок экрана основан на DOM и, как таковой, может не быть на 100% точным к реальному представлению, поскольку он не создает фактического снимка экрана, а создает снимок экрана на основе информации, доступной на странице.

Не требует рендеринга с сервера, так как все изображение создается в браузере клиента. Сам скрипт HTML2Canvas все еще находится в очень экспериментальном состоянии, так как он не анализирует почти столько атрибутов CSS3, сколько я хотел бы, ни поддерживает загрузку изображений CORS, даже если прокси-сервер был доступен.

Все еще довольно ограниченная совместимость браузера (не потому, что больше не может быть поддержано, просто не было времени сделать его более кросс-браузерным).

Для получения дополнительной информации посмотрите примеры здесь:

http://hertzen.com/experiments/jsfeedback/

edit Сценарий html2canvas теперь доступен отдельно здесь и некоторые примеры здесь.

edit 2 Еще одно подтверждение того, что Google использует очень похожий метод (на самом деле, основываясь на документации, единственным существенным отличием является их асинхронный метод обхода/рисования), можно найти в этой презентации Эллиотта Спрена из команды Google Feedback: http://www.elliottsprehn.com/preso/fluentconf/

  • 1
    Очень круто, Sikuli или Selenium могут быть хороши для перехода на разные сайты, сравнивая снимок сайта из инструмента тестирования с визуализированным изображением html2canvas.js с точки зрения сходства пикселей! Интересно, можно ли было бы автоматически пройтись по частям DOM с помощью очень простого решателя формул, чтобы найти способ анализа альтернативных источников данных для браузеров, где getBoundingClientRect недоступен. Я бы, наверное, воспользовался бы этим, если бы это был открытый исходный код, подумывал сам поиграть с ним Отличная работа, Никлас!
  • 1
    @Luke Stanley Я, скорее всего, выложу исходники на github на этих выходных, оставив при этом некоторые мелкие исправления и изменения, которые я хочу сделать до этого, а также избавлюсь от ненужной зависимости jQuery, которая у него есть в настоящее время.
Показать ещё 21 комментарий
58

Теперь ваше веб-приложение может использовать "собственный" снимок экрана для всего рабочего стола клиента, используя getUserMedia():

Взгляните на этот пример:

https://www.webrtc-experiment.com/Pluginfree-Screen-Sharing/

Клиент должен будет использовать хром (на данный момент) и должен будет включить поддержку захвата экрана под флагами chrome://.

  • 11
    Скажите, пожалуйста, почему вы хотите сделать снимок всего моего рабочего стола? Я использовал до сих пор Chrome, теперь я думаю, чтобы удалить его, даже если я удовлетворен его возможностями. Мне как конечному пользователю это не нравится, когда кто-то делает скриншот моего рабочего стола!
  • 2
    Я не могу найти демонстрации, просто сделав снимок экрана - все дело в совместном использовании экрана. придется попробовать.
Показать ещё 5 комментариев
22

Как упоминает Никлас, вы можете использовать библиотеку html2canvas, чтобы сделать скриншот с помощью браузера js. Я разработаю его ответ в этом пункте, предоставив пример создания скриншота с использованием этой библиотеки (в этом фрейме вопроса):

function report() {
  let region = document.querySelector("body"); // whole screen
  html2canvas(region, {
    onrendered: function(canvas) {
      let pngUrl = canvas.toDataURL(); // png in dataURL format
      let img = document.querySelector(".screen");
      img.src = pngUrl; 

      // here you can allow user to set bug-region
      // and send it with 'pngUrl' to server
    },
  });
}
.container {
  margin-top: 10px;
  border: solid 1px black;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/html2canvas/0.4.1/html2canvas.min.js"></script>
<div>Screenshot tester</div>
<button onclick="report()">Take screenshot</button>

<div class="container">
  <img width="75%" class="screen">
</div>

В функции report() onrendered после получения изображения в качестве data-uri, вы можете показать его пользователю и позволить ему нарисовать "область ошибки" мышью, а затем отправить скриншот и координаты области на сервер.

В этом примере была сделана async/await версия: с хорошей makeScreenshot() .

ОБНОВИТЬ

Простой пример, который позволяет вам сделать скриншот, выбрать регион, описать ошибку и отправить запрос POST (здесь jsfiddle) (основная функция - report()).

async function report() {
    let screenshot = await makeScreenshot(); // png dataUrl
    let img = q(".screen");
    img.src = screenshot; 
    
    let c = q(".bug-container");
    c.classList.remove('hide')
        
    let box = await getBox();    
    c.classList.add('hide');

    send(screenshot,box); // sed post request  with bug image, region and description
    alert('To see POST requset with image go to: chrome console > network tab');
}

// ----- Helper functions

let q = s => document.querySelector(s); // query selector helper
window.report = report; // bind report be visible in fiddle html

async function  makeScreenshot(selector="body") 
{
  return new Promise((resolve, reject) => {  
    let node = document.querySelector(selector);
    
    html2canvas(node, { onrendered: (canvas) => {
        let pngUrl = canvas.toDataURL();      
        resolve(pngUrl);
    }});  
  });
}

async function getBox(box) {
  return new Promise((resolve, reject) => {
     let b = q(".bug");
     let r = q(".region");
     let scr = q(".screen");
     let send = q(".send");
     let start=0;
     let sx,sy,ex,ey=-1;
     //console.log({b,r, scr});
     b.addEventListener("click", e=>{
       if(start==0) {
         sx=e.offsetX+scr.offsetLeft;
         sy=e.offsetY+b.offsetTop;
         ex=0;
         ey=0;
         r.style.top = sy +'px';
         r.style.left =sx +'px';     
         r.style.width = 0;
         r.style.height = 0; 
       }
       start=(start+1)%3;  		
     });
     
     b.addEventListener("mousemove", e=>{
       //console.log(e)
       if(start==1) {
           ex=e.offsetX+scr.offsetLeft-sx;
           ey=e.offsetY+b.offsetTop-sy
           r.style.width = ex +'px';
           r.style.height = ey +'px'; 
       }
     });
     
     send.addEventListener("click", e=>{
       start=0;
       let a=100/75 //zoom out img 75%       
       resolve({
          x:Math.floor((sx-scr.offsetLeft)*a),
          y:Math.floor((sy-b.offsetTop)*a),
          width:Math.floor(ex*a),
          height:Math.floor(ey*a),
          desc: q('.bug-desc').value
          });
          
     });
  });
}

function send(image,box) {

    let formData = new FormData();
    let req = new XMLHttpRequest();
    
    formData.append("box", JSON.stringify(box)); 
    formData.append("screenshot", image);     
    
    req.open("POST", '/upload/screenshot');
    req.send(formData);
}
.bug-container { background: rgb(255,0,0,0.1); margin-top:20px; text-align: center; }
.send { border-radius:5px; padding:10px; background: green; cursor: pointer; }
.region { position: absolute; background: rgba(255,0,0,0.4); }
.example { height: 100px; background: yellow; }
.bug { margin-top: 10px; cursor: crosshair; }
.hide { display: none; }
<script src="https://cdnjs.cloudflare.com/ajax/libs/html2canvas/0.4.1/html2canvas.min.js"></script>
<body>
<div>Screenshot tester</div>
<button onclick="report()">Report bug</button>

<div class="example">Lorem ipsum</div>

<div class="bug-container hide">
  <div>Select bug region</div>
  <div class="bug">    
    <img width="75%" class="screen" >
    <div class="region"></div> 
  </div>
  <div>
    <textarea class="bug-desc">Describe bug here...</textarea>
  </div>
  <div class="send">SEND BUG</div>
</div>

</body>
  • 7
    Если вы хотите дать минус балл, оставьте также комментарий с объяснением
  • 0
    Я думаю, что причина того, что вы получили отрицательное голосование, заключается в том, что библиотека html2canvas - это его библиотека, а не инструмент, на который он просто указал.
Показать ещё 1 комментарий

Ещё вопросы

Сообщество Overcoder
Наверх
Меню