JavaScript 实现前端页面截屏与局部截取方案

作者:微光时间:2026-10-22 06:28:47浏览:616

本文介绍如何利用JavaScript在前端实现页面截屏功能。通过原生SVG与Canvas结合的方式解析DOM转图片原理,并重点讲解使用html2canvas库简化开发。针对默认截取全屏产生大量空白的问题,提供了将Canvas转换为Image再重新绘制以截取指定区域的解决方案,适用于如卡组分享等需要生成图片的场景。

前端截屏需求背景

在参与网易炉石盒子页面开发时,针对卡组分享页(地址:炉石盒子卡组分享)提出了一个需求:用户可以将当前卡组以图片形式分享给好友。最初方案是由后端服务器将页面转换为图片并返回地址,前端接收后展示。该方案理论上可行,且服务器可对生成的图片进行缓存,提升后续请求效率。然而,实际运行中发现后端转换的图片与页面内容偶尔存在不一致,部分元素缺失,导致产品经理对此提出整改要求。由于后端转换接口由后台同事负责,且涉及异步加载内容(如底部二维码通过Canvas生成),后端转换结果不稳定,无法可靠截取异步渲染的内容。最终,Leader指示前端尝试直接使用JavaScript进行截图,旨在减轻服务器压力并解决上述Bug。

DOM转Canvas原理

起初认为使用JavaScript进行截图的想法不切实际,主要顾虑在于JS缺乏调用操作系统截图功能的权限,且浏览器BOM未提供原生截图接口。通过搜索“JS html to png”,找到了相关讨论,其中提到将DOM转换为Canvas的思路。进一步搜索“dom to canvas”,查阅了MDN文档“Drawing_DOM_objects_into_a_canvas”。文档指出,不能直接将DOM转为Canvas,但可以先将DOM转为SVG,再将SVG绘制到Canvas中。这种转换方式可能因为SVG使用XML表示且结构与DOM一致而实现。

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

2.需要一个 svg 元素

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

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

将DOM转换为Canvas的过程仅包含上述几个步骤。文档提供了一个简单的示例代码:


<!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元素中,从而截取整个页面?

上述示例仅处理简单的“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>

使用html2canvas简化实现

针对外部样式不生效的问题,理论上可以通过JavaScript遍历所有DOM元素,将计算后的样式通过element.style对象添加到行内样式中。但由于开发时间紧迫,难以自行编写此类函数,因此寻找现成的库。通过搜索发现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进行处理。基本思路是:将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转Canvas的原理。

总结

以上介绍了JavaScript截屏功能的实现代码,希望对大家有所帮助。如有疑问请留言,小编会及时回复。感谢对本站的支持!

作者最新文章