使用HTML5 / Canvas / JavaScript获取浏览器屏幕截图

Using HTML5/Canvas/JavaScript to take in-browser screenshots

Google的"报告错误"或"反馈工具"允许您选择浏览器窗口的一个区域来创建一个屏幕截图,该截图与您对错误的反馈一起提交。

Google Feedback Tool Screenshotjason small的截图,发布在一个重复的问题中。

他们是怎么做到的?Google的javascript反馈API是从这里加载的,它们对反馈模块的概述将演示屏幕截图功能。


javascript可以读取DOM,并使用canvas呈现出一个相当精确的表示。我一直在编写一个将HTML转换为画布图像的脚本。今天决定像您描述的那样执行它来发送反馈。

该脚本允许您创建反馈表单,其中包括在客户端浏览器上创建的屏幕截图以及表单。屏幕截图是基于DOM的,因此可能不完全精确到真实的表示,因为它不会生成实际的屏幕截图,而是基于页面上的可用信息构建屏幕截图。

它不需要从服务器进行任何渲染,因为整个图像是在客户机的浏览器上创建的。HTML2canvas脚本本身仍然处于非常实验的状态,因为它没有解析我希望它解析的CSS3属性,也不支持加载CORS图像,即使有代理可用。

浏览器兼容性仍然相当有限(不是因为无法支持更多功能,只是没有时间让它更受跨浏览器支持)。

有关更多信息,请查看以下示例:

http://hertzen.com/experimentals/jsfedback/

编辑html2canvas脚本现在在这里单独提供,在这里提供一些示例。

编辑2谷歌反馈团队的Elliott Sprehn在本演示文稿中还确认了谷歌使用非常相似的方法(事实上,基于文档,唯一的主要区别是它们的异步遍历/绘制方法):http://www.elliottsprenhn.com/preso/fluentconf/


现在,您的Web应用程序可以使用getUserMedia()对客户端的整个桌面进行"本地"截图:

看看这个例子:

https://www.webrtc-experiment.com/pluginfree-screen-sharing/

客户端必须使用chrome(目前),并且需要在chrome://flags下启用屏幕捕获支持。


正如Niklas提到的,您可以使用html2canvas库在浏览器中使用JS截图。在这一点上,我将通过提供使用此库截图的示例来扩展他的答案:

1
2
3
4
5
6
7
8
9
10
11
12
13
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
    },
  });
}
1
2
3
4
.container {
  margin-top: 10px;
  border: solid 1px black;
}
1
2
3
4
5
6
<script src="https://cdnjs.cloudflare.com/ajax/libs/html2canvas/0.4.1/html2canvas.min.js">
Screenshot tester
<button onclick="report()">Take screenshot</button>


  <img width="75%" class="screen">

onrendered中的report()函数中,获取图像作为数据uri后,可以向用户显示,并允许用户用鼠标绘制"bug区域",然后向服务器发送截图和区域坐标。

在这个例子中,制作了async/await版本:带有漂亮的makeScreenshot()函数。

更新

简单的示例,允许您截图、选择区域、描述错误和发送POST请求(这里是jsFiddle)(主要功能是report())。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
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;
     r.style.width=0;
     r.style.height=0;
     
     let drawBox= () => {
         r.style.left   = (ex > 0 ? sx : sx+ex ) +'px';
         r.style.top    = (ey > 0 ? sy : sy+ey) +'px';
         r.style.width  = Math.abs(ex) +'px';
         r.style.height = Math.abs(ey) +'px';
     }
     
     
     
     //console.log({b,r, scr});
     b.addEventListener("click", e=>{
       if(start==0) {
         sx=e.pageX;
         sy=e.pageY;
         ex=0;
         ey=0;
         drawBox();
       }
       start=(start+1)%3;      
     });
     
     b.addEventListener("mousemove", e=>{
       //console.log(e)
       if(start==1) {
           ex=e.pageX-sx;
           ey=e.pageY-sy
           drawBox();
       }
     });
     
     send.addEventListener("click", e=>{
       start=0;
       let a=100/75 //zoom out img 75%      
       resolve({
          x:Math.floor(((ex > 0 ? sx : sx+ex )-scr.offsetLeft)*a),
          y:Math.floor(((ey > 0 ? sy : sy+ey )-b.offsetTop)*a),
          width:Math.floor(Math.abs(ex)*a),
          height:Math.floor(Math.abs(ex)*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);
}
1
2
3
4
5
6
.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; }
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
<script src="https://cdnjs.cloudflare.com/ajax/libs/html2canvas/0.4.1/html2canvas.min.js">
<body>
Screenshot tester
<button onclick="report()">Report bug</button>

Lorem ipsum


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


</body>