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" width="200" height="200">
</canvas>
<script>
var canvas = ElementById('canvas');
var ctx = Context('2d');
var data = '<svg xmlns="/2000/svg" width="200" height="200">' +
'<foreignObject width="100%" height="100%">' +
'<div xmlns="/1999/xhtml" >' +
'<em>I</em> like ' +
'<span >' +
'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 = ateObjectURL(svg);
ctx.drawImage(img, 0, 0);
}
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" width=200" height="200">
</canvas>
<script>
var canvas = ElementById('canvas');
var ctx = Context('2d');
var data = '<svg xmlns="/2000/svg" width="200" height="200">' +
'<foreignObject width="100%" height="100%">' +
'<div xmlns="/1999/xhtml" >' +
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 = ateObjectURL(svg);
ctx.drawImage(img, 0, 0);
}
img.src = url;
</script>
</body>
</html>
既然外部样式不⽣效,那我们可以通过JS遍历所有的dom元素,把全部的样式通过element.style对象添加到⾏内样式啊。这个思路听起来不错,但是,实现这个把外部样式转成⾏内样式的函数我还真写不出来啊。需求⽐较紧,也没有那多时间去瞎折腾了,所以,就想有没有现成的库。于是⼜去goo
gle⼀下。很幸运,⼀下⼦就搜到了⼀个叫做html2canvas的库,⾮常棒的⼀个库,很强⼤、但⽤法⾮常简单.就这么简单的⽅法,就可以把我的整个页⾯截图下来了:
function convertHtml2Canvas() {
html2canvas(document.body, {
svg canvas
allowTaint: false,
taintTest: true
}).then(function(canvas) {
document.body.appendChild(canvas);
}).catch(function(e) {
<('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 = DataURL("image/png", 0.1);
return image;
}
// Converts image to canvas; returns new canvas element
function convertImageToCanvas(image, startX, startY, width, height) {
var canvas = ateElement("canvas");
canvas.width = width;
canvas.height = height;
return canvas;
}
然后,再把我们上⾯的写的 convertHtml2Canvas 改成下⾯的:
function convertHtml2Canvas() {
html2canvas(document.body, {
allowTaint: false,
taintTest: true
}).then(function(canvas) {
var img = convertCanvasToImage(canvas);
document.body.appendChild(img);
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) {
<('error', e);
});
}
这时候就可以把它的页⾯的某部分内容进⾏截取下来了。效果如卡组分享测试页⾯。页⾯左边部分是DOM结构的,右边部分是则是使⽤html2canvas转换出来的图⽚。长得⼀模⼀样,毫⽆⽑病哈。
关于JS页⾯截图的就写到这⾥啦,因为也只刚刚接触,很多东西也理解得不到位,欢迎各⼤神指点。后⾯会深⼊学习⼀下html2canvas的源码,进⼀步理解dom to canvas的原理。
总结
以上所述是⼩编给⼤家介绍的JavaScript截屏功能的实现代码,希望对⼤家有所帮助,如果⼤家有任何疑问请给我留⾔,⼩编会及时回复⼤家的。在此也⾮常感谢⼤家对⽹站的⽀持!

版权声明:本站内容均来自互联网,仅供演示用,请勿用于商业和其他非法用途。如果侵犯了您的权益请与我们联系QQ:729038198,我们将在24小时内删除。