1054

谷歌的“报告错误”或“反馈工具”允许你在浏览器窗口中选择一个区域,创建一个屏幕截图,与你提交的关于错误的反馈一起提交。

谷歌反馈工具截图 Jason Small的截图,发布在重复的问题.

他们是怎么做到的?谷歌的JavaScript反馈API加载自在这里他们对反馈模块的概述将演示屏幕截图功能。

7
  • 4
    埃利奥特·斯普伦在推文中写道几天前:>@CatChen那篇stackoverflow帖子不准确。谷歌反馈的屏幕截图完全由客户端完成。:) 评论 2011年7月11日17:53
  • 1
    这很符合逻辑,因为他们希望准确捕捉用户浏览器如何呈现页面,而不是如何使用引擎在服务器端呈现页面。如果只将当前页面DOM发送到服务器,它将忽略浏览器呈现HTML的方式中的任何不一致。这并不意味着陈的答案是错误的截图,只是看起来谷歌在用不同的方式。 评论 2011年7月11日18:21
  • Elliott今天提到了Jan Kuča,我在Jan的推文中找到了这个链接:jankuca.tumblr.com/post/7391640769/…
    – 陈猫
    评论 2011年7月12日11:28
  • 稍后我将深入研究这一点,看看如何使用客户端渲染引擎实现它,并检查谷歌是否真的这样做。
    – 陈猫
    评论 2011年7月12日11:30
  • 我看到了compareDocumentPosition、getBoxObjectFor、toDataURL、drawImage、跟踪填充等的使用。尽管如此,仍有数千行模糊代码需要去模糊化和浏览。我很想看到它的开源授权版本,我已经联系了Elliott Sprehn! 评论 2011年7月12日18:18

8个答案8

重置为默认值
1263
+50

JavaScript可以读取DOM并使用帆布。我一直在编写一个将HTML转换为画布图像的脚本。今天决定将其实现为像您描述的那样发送反馈。

该脚本允许您创建反馈表单,其中包括在客户端浏览器上创建的屏幕截图以及表单。屏幕截图基于DOM,因此可能无法100%准确地反映实际情况,因为它不会生成实际屏幕截图,而是基于页面上的可用信息构建屏幕截图。

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

浏览器兼容性仍然非常有限(不是因为无法支持更多浏览器,只是还没有时间使其更受跨浏览器支持)。

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

http://hertzen.com/experiats/jsfeedback/

编辑html2canvas脚本现在可以单独使用在这里还有一些此处的示例.

编辑2Google Feedback团队的Elliott Sprehn在本演示中再次证实了Google使用了非常类似的方法(事实上,根据文档,唯一的主要区别是它们的异步遍历/绘制方法):http://www.elliottsprehn.com/preso/fluntconf/

22
  • 1
    很酷,Sikuli或Selenium可能适合访问不同的站点,将测试工具中的站点快照与html2canvas.js渲染的图像进行像素相似性比较!想知道您是否可以使用一个非常简单的公式解算器自动遍历DOM的各个部分,以了解如何解析getBoundingClientRect不可用的浏览器的备用数据源。如果它是开源的,我可能会使用它,我正在考虑自己玩弄它。干得好Niklas! 评论 2011年7月15日19:25
  • 1
    @Luke Stanley我很可能会在这个周末在github上发布源代码,在那之前我想做一些小的清理和更改,并消除它目前不必要的jQuery依赖性。 评论 2011年7月15日19:43
  • 54
    源代码现在位于github.com/niklasvh/html2canvas,正在使用的脚本的一些示例html2canvas.hertzen.com那里。仍然有很多bug需要修复,所以我不建议在实时环境中使用该脚本。 评论 2011年7月16日1:42
  • 2
    任何解决方案都会对SVG有很大帮助。它不适用于highcharts.com 评论 2012年12月25日6:26
  • 5
    @我看到你的例子变成了一个真正的项目。也许可以更新你对该项目实验性质最受欢迎的评论。在将近900次提交之后,我认为这不仅仅是一次实验;-)
    – 场外
    评论 2018年12月21日8:59
79

您的web应用程序现在可以使用获取用户媒体():

看看这个例子:

https://www.webrtc-experiat.com/Pluginfree-Screen-Sharing网站/

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

7
  • 我找不到任何只是截屏的演示——一切都是关于屏幕共享的。我得试试。
    – jwl公司
    评论 2014年10月16日14:56
  • 8
    @XMight,您可以通过切换屏幕捕获支持标志来选择是否允许这样做。 评论 2014年11月10日15:20
  • 22
    @X好吧,请不要这样想。Web浏览器应该能够做很多事情,但不幸的是,它们与实现不一致。如果浏览器有这样的功能,只要用户被要求,这是完全可以的。如果没有你的注意,没有人能拍出截图。但是太多的恐惧会导致糟糕的实现,比如剪贴板API,它已经被完全禁用,而不是创建确认对话框,比如网络摄像头、麦克风、屏幕截图功能等。 评论 2017年5月24日17:50
  • 4
    这已被弃用,将根据developer.mozilla.org/en-US/docs/Web/API/Navigator/getUserMedia 评论 2018年2月16日8:10
  • 12
    @阿古斯汀·考廷导航器.getUserMedia()已弃用,但下面写着“……请使用较新的navigator.mediaDevices.getUserMedia()”,即它刚刚被更新的API所取代。
    – 左旋派
    评论 2019年5月3日18:17
60

使用获取Canvas或Jpeg Blob/ArrayBuffer的屏幕截图获取显示媒体应用程序编程接口:

固定装置1:仅对Electron.js使用带有chromeMediaSource的getUserMedia
固定装置2:引发错误而返回空对象
固定装置3:修复演示以防止错误:必须从用户手势处理程序调用getDisplayMedia

//文档:https://developer.mozilla.org/en-US/docs/Web/API/MediaDevices/getDisplayMedia//请参见:https://www.webrtc-experiat.com/Pluginfree-Screen-Sharing/#20893521368186473//请参见:https://github.com/muaz-khan/WebRTC-Experiment/blob/master/Pluginfree-Screen-Sharing/conference.js函数getDisplayMedia(选项){if(navigator.mediaDevices&&navigator.mediaDevices.getDisplayMedia){return navigator.mediaDevices.getDisplayMedia(选项)}if(navigator.getDisplayMedia){return navigator.getDisplayMedia(选项)}if(导航器.webkitGetDisplayMedia){return navigator.webkitGetDisplayMedia(选项)}if(navigator.mozGetDisplayMedia){return navigator.mozGetDisplayMedia(选项)}throw new Error(“getDisplayMedia未定义”)}函数getUserMedia(选项){if(navigator.mediaDevices&&navigator.mediaDevices.getUserMedia){return navigator.mediaDevices.getUserMedia(选项)}if(导航器.getUserMedia){return navigator.getUserMedia(选项)}if(导航器.webkitGetUserMedia){return navigator.webkitGetUserMedia(选项)}if(navigator.mozGetUserMedia){return navigator.mozGetUserMedia(选项)}抛出新错误(“未定义getUserMedia”)}异步函数takeScreenshotStream(){//请参阅:https://developer.mozilla.org/en-US/docs/Web/API/Window/screen(https://developer.mozilla.org/en-US/docs/Web/API/Window/screen)常量宽度=屏幕宽度*(window.devicePixelRatio||1)常量高度=屏幕高度*(window.devicePixelRatio||1)常量错误=[]让溪流尝试{流=等待getDisplayMedia({音频:假,//请参见:https://developer.mozilla.org/en-US/docs/Web/API/MediaStreamConstraints/video视频:{宽度,高度,帧速率:1,},})}捕获(ex){errors.push(ex)}//对于电子jsif(navigator.userAgent.indexOf('Electron')>=0){尝试{流=等待getUserMedia({音频:假,视频:{强制性:{chromeMediaSource:'桌面',//chromeMediaSourceId:源id,minWidth:宽度,maxWidth:宽度,minHeight:高度,maxHeight:高度,},},})}捕获(ex){errors.push(ex)}}if(errors.length){console.debug(…错误)if(!流){抛出错误[errors.length-1]}}回流}异步函数takeScreenshotCanvas(){const stream=等待截图流()//发件人:https://stackoverflow.com/a/57665309/5221762const-video=document.createElement('video')const result=等待新承诺((resolve,reject)=>{video.onloadedmetadata=()=>{video.play()video.pause()//发件人:https://github.com/kasprownik/elecron-screencapture/blob/master/index.jsconst canvas=document.createElement('canvas')canvas.width=video.videoWidthcanvas.height=video.videoHeightconst上下文=canvas.getContext('2d')//请参见:https://developer.mozilla.org/en-US/docs/Web/API/HTMLVideoElement网站context.drawImage(视频,0,0,video.videoWidth,video.jevoHeight)解析(画布)}video.srcObject=流})stream.getTracks().forEach(函数(track)){track.stop()})if(结果==空){throw new Error(“无法拍摄画布屏幕截图”)}返回结果}//发件人:https://stackoverflow.com/a/46182044/5221762函数getJpegBlob(画布){return new Promise((resolve,reject)=>{//文档:https://developer.mozilla.org/en-US/docs/Web/API/HTMLCanvasElement/toBlobcanvas.toBlob(blob=>解析(blob),'image/jpeg',0.95)})}异步函数getJpegBytes(画布){const-blob=等待getJpegBlob(画布)return new Promise((resolve,reject)=>{const fileReader=新fileReader()fileReader.addEventListener('loadend',函数(){if(此错误){拒绝(this.error)返回}解析(this.result)})fileReader.readAsArrayBuffer(blob)})}异步函数takeScreenshotJpegBlob(){const canvas=等待截图canvas()return getJpegBlob(画布)}异步函数takeScreenshotJpegBytes(){const canvas=等待takeScreenshotCanvas()return getJpegBytes(画布)}函数blobToCanvas(blob、maxWidth、maxHeight){return new Promise((resolve,reject)=>{const img=新图像()img.onload=函数(){const canvas=document.createElement('canvas')常数刻度=数学.min(1,最大宽度?maxWidth/img.width:1,最大高度?maxHeight/img.height:1,)canvas.width=img.width*比例canvas.height=img.height*比例const-ctx=canvas.getContext('2d')ctx.drawImage(img,0,0,img.width,img.height,0,O,canvas.width和canvas.height)解析(画布)}img.onerror=()=>{拒绝(新错误(“错误将blob加载到图像”)}img.src=URL.createObjectURL(blob)})}

演示:

document.body.onclick=异步()=>{//截图var screenshotJpegBlob=等待take截图Jpegblob()//显示最大尺寸为300 x 300像素的预览var previewCanvas=等待blobToCanvas(屏幕截图JpegBlob,300,300)previewCanvas.style.position=“已修复”document.body.appendChild(预览画布)//将其发送到服务器var formdata=新formdata()formdata.append(“屏幕截图”,屏幕截图JpegBlob)等待获取('https://your-web-site.com网站/', {方法:“POST”,正文:表单数据,'内容类型':“multipart/form-data”,})}//然后单击页面
8
  • 1
    奇怪为什么只有1票赞成,这真的很有帮助! 评论 2020年2月28日22:17
  • 请问它是如何工作的?你能为像我这样的新手提供一个演示吗?谢谢 评论 2020年4月18日10:25
  • @kabrice我添加了一个演示。只需将代码放入Chrome控制台即可。如果需要旧浏览器支持,请使用:babeljs.io/en/repl 评论 2020年4月19日4:19
  • 1
    这是我在火狐里拍的一张照片。。。只是不允许使用摄像头:) 评论 2020年9月6日17:08
  • 4
    我喜欢这个答案,但不幸的是,它在屏幕截图中包含了“选择要共享的屏幕”对话框,并且覆盖了屏幕的很大一部分。至少对我使用Chrome来说是这样。 评论 2020年11月5日19:51
56

PoC公司

作为Niklas提到您可以使用html2画布库在浏览器中使用JS截屏。在这一点上,我将通过提供一个使用此库截屏的示例来扩展他的答案(“概念证明”):

函数报告(){let region=document.querySelector(“body”);//全屏幕html2画布(地区{onrendered:函数(画布){let pngUrl=画布.toDataURL();//dataURL格式的png让img=document.querySelector(“.screen”);img.src=pngUrl;//在这里,您可以允许用户设置错误区域//并用“pngUrl”将其发送到服务器},});}
.容器{边距:10px;边框:实心1px黑色;}
<script src=“https://cdnjs.cloudflare.com/ajax/libs/html2canvas/0.4.1/html2canvas.min.js“></script>屏幕截图测试仪<button onclick=“report()”>截屏<div class=“container”><img width=“75%”class=“screen”></div>

报告()中的函数已呈现在获得作为数据URI的图像后,您可以将其显示给用户,并允许他用鼠标绘制“错误区域”,然后向服务器发送屏幕截图和区域坐标。

这个例子 异步/等待版本制作:带有漂亮的制作屏幕截图()功能.

更新

一个简单的示例,可以让您截屏、选择区域、描述错误并发送POST请求(这里是jsfiddle)(主要功能是报告()).

异步函数报告(){let screenshot=等待makeScreenshot();//png数据URL设img=q(“.screen”);img.src=屏幕截图;设c=q(“.bug-container”);c.classList.remove(“隐藏”)let box=等待getBox();c.classList.add('hide');发送(屏幕截图,方框);//带有错误图像、区域和描述的sed发布请求alert(“要查看带有图像的POST请求,请转到:chrome控制台>网络选项卡”);}//-----助手功能让q=s=>document.querySelector;//查询选择器助手window.report=报告;//绑定报表在fiddle html中可见异步函数makeScreenshot(选择器=“body”){return new Promise((resolve,reject)=>{let节点=document.querySelector(选择器);html2canvas(节点,{onrendered:(画布)=>{让pngUrl=canvas.toDataURL();解析(pngUrl);}});  });}异步函数getBox(box){return new Promise((resolve,reject)=>{设b=q(“.bug”);设r=q(“.region”);设scr=q(“.screen”);让send=q(“.send”);设start=0;设sx,sy,ex,ey=-1;r.style.width=0;r.style.height=0;让drawBox=()=>{r.style.left=(ex>0?sx:sx+ex)+'px';r.style.top=(ey>0?sy:sy+ey)+'px';r.style.width=数学.abs(ex)+'px';r.style.height=数学.abs(ey)+'px';}//控制台.log({b,r,scr});b.addEventListener(“点击”,e=>{if(开始==0){sx=e.pageX;sy=e.pageY;ex=0;ey=0;drawBox();}开始=(开始+1)%3;});b.addEventListener(“mousemove”,e=>{//控制台.log(e)if(开始==1){ex=e.pageX-sx;ey=e.pageY-sydrawBox();}});send.addEventListener(“点击”,e=>{开始=0;设a=100/75//缩小img 75%解决({x: 数学地板(((ex>0?sx:sx+ex)-scr.offsetLeft)*a),y: 数学地板(((ey>0?sy:sy+ey)-b.offsetTop)*a),宽度:数学地板(数学abs(ex)*a),高度:数学地板(数学abs(ex)*a),desc:q('.bug-desc').value});});});}函数发送(图像、方框){让formData=新formData();让req=新的XMLHttpRequest();formData.append(“框”,JSON.stringify(框));formData.append(“屏幕截图”,图片);req.open(“POST”,“/上传/屏幕截图”);请求发送(formData);}
.bug容器{background:rgb(255,0,0.1);页边空白顶部:20px;文本对齐:居中;}.send{边框半径:5px;填充:10px;背景:绿色;光标:指针;}.region{位置:绝对;背景:rgba(255,0,0,0.4);}.example{高度:100px;背景:黄色;}.bug{margin-top:10px;光标:十字线;}.hide{显示:无;}.screen{指针事件:无}
<script src=“https://cdnjs.cloudflare.com/ajax/libs/html2canvas/0.4.1/html2canvas.min.js“></script><body>屏幕截图测试仪<button onclick=“report()”>报告错误Lorem ipsum<div class=“bug-container hide”><div>选择错误区域:单击一次-移动鼠标-再次单击<div class=“bug”><img width=“75%”class=“screen”><div class=“region”></div><div><textarea class=“bug-desc”>在此处描述错误</文本区域></div>发送BUG</div></body>

5
  • 2
    我认为你被否决的原因很可能是html2canvas库是他的库,而不是他简单指出的工具。 评论 2018年12月20日19:08
  • 如果您不想捕获后期处理效果(作为模糊过滤器),这很好。 评论 2019年1月21日11:54
  • 1
    限制脚本使用的所有图像都需要驻留在同一来源下,以便它能够在没有代理的帮助下读取它们。类似地,如果页面上有其他画布元素被跨源内容污染,它们将变脏,html2canvas无法再读取。 评论 2019年8月5日8:59
  • 在我的网站上使用你的代码,我收到了错误未捕获(承诺中)错误:元素未附加到文档→啊,找到了解决方案:stackoverflow.com/q/64716679/1066234
    – 阿凡达
    评论 2022年11月27日15:17
  • 1
    旁注:已呈现未触发,因为它不存在于html2画布1.4.1不再。
    – 阿凡达
    评论 2022年11月27日15:22
28

下面是2021年使用chrome的完整屏幕截图示例。最终结果是一个待传输的blob。流程是:请求媒体>抓取帧>绘制到画布>传输到blob。如果你想做更节省内存的探索屏幕外画布或者可能图像位图渲染上下文

https://jsfiddle.net/v24hyd3q/1/

//请求媒体navigator.mediaDevices.getDisplayMedia().then(stream=>{//从流中抓取框架let track=流.getVideoTracks()[0];let capture=新图像捕获(曲目);capture.grabFrame().then(位图=>{//停止共享track.stop();//将位图绘制到画布canvas.width=位图宽度;canvas.height=位图高度;canvas.getContext('2d').drawImage(位图,0,0);//从画布抓取水滴canvas.toBlob(blob=>{//在这里处理blobconsole.log('输出blob:',blob);});});}).catch(e=>控制台.log(e));
1
  • 那把小提琴就是信息!好东西。考虑将其余代码添加到示例中。最值得注意的是<canvas id='canvas'/>标签。 评论 2022年12月9日10:17
16

下面是一个使用的示例:获取显示媒体

document.body.innerHTML='<video style=“宽度:100%;高度:100%;边框:1px黑色实心;”/>';navigator.mediaDevices.getDisplayMedia().then(媒体流=>{const-video=document.querySelector('video');video.srcObject=媒体流;video.onloadedmetadata=e=>{video.play();video.pause();};}).catch(err=>console.log(`${err.name}:${err.message}`));

值得一看的是屏幕捕获API文档。

0

你可以试试我的新JS库:屏幕截图.js.

它可以拍摄真实的屏幕快照。

加载脚本:

<script src=“https://raw.githubusercontent.com/amiad/screenshot.js/master/screenshost.js“></script>

并截屏:

新屏幕截图({success:img=>{//回调函数myimage=图像;}});

您可以在项目页面中阅读更多选项。

  • 1
    他问他们是怎么做到的。没有图书馆可以这样做。
    – 格诺珀
    评论 2022年7月14日9:28
  • 谢谢你提供了很棒的图书馆。不幸的是,它需要授予访问该选项卡的权限。 评论 2023年5月1日12:04
  • 是的,浏览器需要允许访问屏幕截图。
    – 阿米德
    评论 2023年5月1日16:26
0

此方法使用流行的JS库html2canvas从网页捕获屏幕截图。

该脚本实现了以下步骤,以从页面HTML中捕获屏幕截图。

  • 它初始化html2canvas库类并向其提供主体HTML。
  • 生成画布元素并附加到HTML。
  • 它从画布对象获取图像源数据URL。
  • 使用Ajax将图像推送到服务器。

capture-screenshot是点击按钮的id,我们将捕获DOM的屏幕截图。

<脚本src=“https://ajax.googleapis.com/ajax/libs/jquery/3.2.1/jquery.min.js“></script><script type=“text/javascript”src=“https://html2canvas.hertzen.com/dist/html2chanvas.min.js“></script><script type=“text/javascript”>$(“#capture-screenshot”).单击(function(){const screenshotTarget=文档.body;html2canvas(屏幕截图目标)。然后(画布=>{//将图像作为png在行下使用//const base64image=canvas.toDataURL(“image/png”);//在窗口中使用下面的行显示图像//window.location.href=base64图像;//屏幕截图作为画布附加到正文document.body.appendChild(画布);dataURL=画布.toDataURL();//要在控制台中打印屏幕截图,请使用下面的行//console.log(dataURL);//以下行是可选的,用于保存屏幕截图//在服务器端。它启动一个ajax调用将屏幕截图推送到服务器(dataURL);});  });函数pushScreenshotToServer(数据URL){$.ajax({url:“push-screenshot.php”,类型:“POST”,数据:{image:dataURL},  dataType:“html”,成功:function(){console.log('推送到服务器的屏幕截图');}  });  }  </script>

不是你想要的答案吗?浏览标记的其他问题问你自己的问题.