JavaScript截屏功能的实现代码

最近参与了网易炉石盒子的相关页面开发,在做卡组分享页(地址:炉石盒子卡组分享),有个需求:用户可以把这个卡组以图片的形式分享给好友。最初的的做法是使用服务器把该页面转换成图片,然后把图片地址返回给前端。嗯,这样也挺好的啊,而且服务器还可以对转换出来的图片进行缓存,下次请求可以直接返回图片地址了。原理上是毫无毛病的。然而,问题来了,后台转换的图片和页面内容偶尔不一致,有时候会少了一一些内容,PM姐姐就很不爽了,说这个问题一定要解决。反正页面转成图片的接口是后台做的,关我luan事啊!就在暗暗自喜的时候,悲催的事情发生的,后台的同事说,因为页面里面有些内容是异步加载出来的(比如底部的二维码是通过canvas生成的),服务器转换不稳定,有时候对异步渲染的内容无法截取。说白了,就是这问题他没有办法解决,前端去改吧,谁叫前端用了异步渲染呢?最后Leader让我尝试能不能直接用JS进行截图了,这样既可以减轻服务器的压力,又可以解决上面bug。

  一开始,我觉得使用JS截图的想法是非常荒谬的(怪我无知咯,前端这几年发展的实在太快了):首先JS没有权限调用操作系统的截图功能,其次,浏览器(BOM)也没有提供相关的截图接口。我该怎么办呢、怎么办呢?有事找Google啊。然后搜索了一下: JS html to png ,然后来到就找到了这里:render-html-to-an-image。开始有思路了,回答中有人提到可以把dom转成canvas,嗯!又是Canvas!我不由得兴奋起来,真的是山重水复疑无路,柳暗花明又一村啊!然后再搜索一下 dom to canvas,来到了大家熟知的mdn的文档Drawing_DOM_objects_into_a_canvas。然后就开始认(zhuang)真(bi)的看文档。文档开头就说到,不可以把dom转成canvas,但是可以把dom转成svg,然后再把svg画到canvas里面去。也许有人会问,为什么要先把dom转成svg呢?这可能是因为svg使用xml表示、结构和dom一致吧。
下面就是官方文档的step by step的教程:

1.Blob的媒体类型必须是"image/svg+xml"

2.需要一个 svg 元素

3.在 svg 元素里面插入一个 foreignObject 元素

4.在 foreignObject 元素里面放入符合规范的 html

把dom转成canvas就这么简单,就上面几个步骤。下面是文档给出的一上简单的demo:

<!doctype html>
<html lang="en">
<head>
 <meta charset="UTF-8">
 <title>Document</title>
</head>
<body>
<canvas id="canvas" style="border:2px solid black;" width="200" height="200">
</canvas>
<script>
 var canvas = document.getElementById('canvas');
 var ctx = canvas.getContext('2d');
 var data = '<svg xmlns="http://www.w3.org/2000/svg" width="200" height="200">' +
  '<foreignObject width="100%" height="100%">' +
  '<div xmlns="http://www.w3.org/1999/xhtml" style="font-size:40px">' +
  '<em>I</em> like ' +
  '<span style="color:white; text-shadow:0 0 2px blue;">' +
  'cheese</span>' +
  '</div>' +
  '</foreignObject>' +
  '</svg>';
 var DOMURL = window.URL || window.webkitURL || window;
 var img = new Image();
 var svg = new Blob([data], {type: 'image/svg+xml'});
 var url = DOMURL.createObjectURL(svg);
 img.onload = function() {
  ctx.drawImage(img, 0, 0);
  DOMURL.revokeObjectURL(url);
 }
 img.src = url;
</script>
</body>
</html>

复制代码,运行一下,哇,帅呆了,浏览器上出现了超酷的两行艺术字呢!

嗯,原来dom转成canvas这么简单啊?那我通过 document.body.innerHTML 把body里面的所有dom取出来,然后放到 foreignObject 元素里面,不就OK了、把整个页面都截取下来了吗?

demo仅仅是个Hello World,但是实际项目中的Dom结构比这个复杂多了,比如,引入了外部样式表、图片、而且还可能某些标签不符合xml规范(如缺少闭合标签等)。下面的举个简单的例子,.container不是使用行内样式的,而是在style标签里面定义,字体红色,转成图片后,样式不生效。

<!doctype html>
<html lang="en">
<head>
 <meta charset="UTF-8">
 <title>Document</title>
 <style>
  .container {
   color: red;
  }
 </style>
</head>
<body>
<div class="container" >
 Hello World!
</div>
<canvas id="canvas" style="border:2px solid black;" width=200" height="200">
</canvas>
<script>
 var canvas = document.getElementById('canvas');
 var ctx = canvas.getContext('2d');
 var data = '<svg xmlns="http://www.w3.org/2000/svg" width="200" height="200">' +
  '<foreignObject width="100%" height="100%">' +
  '<div xmlns="http://www.w3.org/1999/xhtml" style="font-size:40px">' +
  document.querySelector('.container').innerHTML +
  '</div>' +
  '</foreignObject>' +
  '</svg>';
 var DOMURL = window.URL || window.webkitURL || window;
 var img = new Image();
 var svg = new Blob([data], {type: 'image/svg+xml'});
 var url = DOMURL.createObjectURL(svg);
 img.onload = function() {
  ctx.drawImage(img, 0, 0);
  DOMURL.revokeObjectURL(url);
 }
 img.src = url;
</script>
</body>
</html>

既然外部样式不生效,那我们可以通过JS遍历所有的dom元素,把全部的样式通过element.style对象添加到行内样式啊。这个思路听起来不错,但是,实现这个把外部样式转成行内样式的函数我还真写不出来啊。需求比较紧,也没有那 多时间去瞎折腾了,所以,就想找找有没有现成的库。于是又去google一下。很幸运, 一下子就搜到了一个叫做html2canvas的库,非常棒的一个库,很强大、但用法非常简单.就这么简单的方法,就可以把我的整个页面截图下来了:

function convertHtml2Canvas() {
  html2canvas(document.body, {
   allowTaint: false,
   taintTest: true
  }).then(function(canvas) {
   document.body.appendChild(canvas);
  }).catch(function(e) {
   console.error('error', e);
  });
 }

目前还有一个问题,就是这种方法默认是把整个页面截取下来的(就是说,会以你的innerHeight和innerWidth为边界,会存在大量的空白),可是,我的卡组只是占了页面的一小部分,我只想要卡组的部分啊。其实已经有了canvas就好办了,我们可以对它进行处理啊。大概思路是:1.把上面得到的canvas对象转成Blob并放到一个img元素。然后再把img.src绘制到canvas里面。这时候调用canvas.drawImage方法就可以截取我们想要的内容了。下面的两个函数分别是把canvas转成image以及反过来把image转成canvas。

// Converts canvas to an image
 function convertCanvasToImage(canvas) {
  var image = new Image();
  image.src = canvas.toDataURL("image/png", 0.1);
  return image;
 }
 // Converts image to canvas; returns new canvas element
 function convertImageToCanvas(image, startX, startY, width, height) {
  var canvas = document.createElement("canvas");
  canvas.width = width;
  canvas.height = height;
  canvas.getContext("2d").drawImage(image, startX, startY, width, height, 0, 0, width, height);
  return canvas;
 }

然后,再把我们上面的写的 convertHtml2Canvas 改成下面的:

function convertHtml2Canvas() {
  html2canvas(document.body, {
   allowTaint: false,
   taintTest: true
  }).then(function(canvas) {
   var img = convertCanvasToImage(canvas);
   document.body.appendChild(img);
   img.onload = function() {
    img.onload = null;
    canvas = convertImageToCanvas(img, 0, 0, 384, 696);
    img.src = convertCanvasToImage(canvas).src;
    $(img).css({
     display: 'block',
     position: 'absolute',
     top: 0,
     left: 400 + 'px'
    });
   }
  }).catch(function(e) {
   console.error('error', e);
  });
 }

这时候就可以把它的页面的某部分内容进行截取下来了。效果如卡组分享测试页面。页面左边部分是DOM结构的,右边部分是则是使用html2canvas转换出来的图片。长得一模一样,毫无毛病哈。

关于JS页面截图的就写到这里啦,因为也只刚刚接触,很多东西也理解得不到位,欢迎各大神指点。后面会深入学习一下html2canvas的源码,进一步理解dom to canvas的原理。

总结

以上所述是小编给大家介绍的JavaScript截屏功能的实现代码,希望对大家有所帮助,如果大家有任何疑问请给我留言,小编会及时回复大家的。在此也非常感谢大家对我们网站的支持!

时间: 2017-07-25

js利用clipboardData实现截屏粘贴功能

本文实例为大家分享了clipboardData截屏粘贴实现代码,供大家参考,具体内容如下 <!DOCTYPE html> <html> <head lang="en"> <meta charset="UTF-8"> <title>copyimg</title> <style type="text/css"> #box{ width:200px; height:20

让编辑器支持word复制黏贴、截屏的js代码

chrome有很多人性化的API,比如拖拽, 比如图片可以转化为base64等: 比如知乎上面的回复中可以直接黏贴图片,  就不需要手动点击图片上传按钮, 选择图片, 确认上传等等: 知乎参考地址:打开 让编辑器支持word的复制黏贴, 其中图片会转化为base64编码, 如果是通过远程打开这个静态页, 黏贴word文档的时候, 图片不会黏贴进来, 因为远程地址无法访问本地磁盘的绝对路径, 如果把下面代码保存成静态界面打开, 那么word中的图片都可以看见, 而且都会被转化为base64编码:

IOS 开发之ios视频截屏的实现代码

IOS 开发之ios视频截屏的实现代码 现在好多视频截屏软件,这里提供一个IOS 视频截屏的方法,大家可以参考下, 实现代码: //截屏 static int i=0; -(IBAction)screenShot:(id)sender{ UIGraphicsBeginImageContextWithOptions(CGSizeMake(640, 960), YES, 0); [[self.window layer] renderInContext:UIGraphicsGetCurrentCont

当前页禁止复制粘贴截屏代码小集

复制代码 代码如下: <style>@media print{body{display:none}}</style> <%--禁止截屏,通过清除剪切板实现 Start--%> <script type="text/javascript"> function clearShearPlate() { try { if (clipboardData.getData("Text") || clipboardData.getDa

android截屏功能实现代码

android开发中通过View的getDrawingCache方法可以达到截屏的目的,只是缺少状态栏! 原始界面 截屏得到的图片 代码实现 1. 添加权限(AndroidManifest.xml文件里) 复制代码 代码如下: <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" /> 2. 添加1个Button(activity_main.xml文件) <RelativeL

表单提交时自动复制内容到剪贴板的js代码

<html> <head> <meta http-equiv="Content-Type" content="text/html; charset=gb2312"> <title>交时自动复制到剪贴板</title> </head> <script language="JavaScript"> /*功能:提交时自动复制到剪贴板 */ function copyTe

Android中关于屏幕的三个小众知识(宽屏适配、禁止截屏和保持屏幕常亮)

前言 宽屏适配.禁止截屏和保持屏幕常亮,这三个与屏幕有关的 Android 开发小众知识,说不定什么时候就派上用场. 宽屏适配 Android的屏幕适配一直以来都在折磨着我们Android开发者,越来越多的手机厂商趋向于全面屏设计,比如今年出厂的 18.5:9 屏幕比例的三星 Galaxy S8 手机,前两天刚发布 18:9 屏幕比例的 OnePlus 5T 手机, 屏幕比例均超过过去常见的 16:9 比例.超大屏幕比例的设计对于 Android App 来说,存在一个屏幕适配的问题. 我们需要

Android源码解析之截屏事件流程

今天这篇文章我们主要讲一下Android系统中的截屏事件处理流程.用过android系统手机的同学应该都知道,一般的android手机按下音量减少键和电源按键就会触发截屏事件(国内定制机做个修改的这里就不做考虑了).那么这里的截屏事件是如何触发的呢?触发之后android系统是如何实现截屏操作的呢?带着这两个问题,开始我们的源码阅读流程. 我们知道这里的截屏事件是通过我们的按键操作触发的,所以这里就需要我们从android系统的按键触发模块开始看起,由于我们在不同的App页面,操作音量减少键和电

Android实现截屏与截长图功能

本文实例为大家分享了Android实现截屏与截长图功能展示的具体代码,供大家参考,具体内容如下 Demo在GitHub的地址:ScreenShoot Demo在CSDN上的下载地址:Android实现截屏与截长图功能 在Android开发中,有时候会遇到需要截屏分享到朋友圈或者QQ,截屏有截取当前屏幕,也有需要截取不仅一个屏幕,可能会很长. 截取当前屏幕并保存到内存卡的方法: // 获取指定Activity的截屏,保存到png文件 public static Bitmap takeScreenS

从零开始用electron手撸一个截屏工具的示例代码

最近在尝试利用 electron 将一个 web 版的聊天工具包装成一个桌面 APP.作为一个聊天工具,截屏可以说是一个必备功能了.不过遗憾的是没有找到很成熟的库来用,也可能是打开方式不对,总之呢没看到现成的,于是就想从头撸一个简单的截图工具.下面就进入正题吧! 思路 electron 提供了截取屏幕的 API,可以轻松的获取每个屏幕(存在外接显示器的情况)和每个窗口的图像信息. 把图片截取出来,然后创建一个全屏的窗口盖住整个屏幕,将截取的图片绘制在窗口上,然后再覆盖一层黑色半透明的元素,看起来