最近遇到了需要把网页下载为图片和打印为PDF的需求,特此整理。
一、借助浏览器
一般浏览器都自带PDF打印功能:
至于保存为图片,有很多浏览器插件(比如screenshot、花瓣)等,都可以将网页保存为图片。
缺点:
- 网页中的图标、背景等可能无法打印成PDF,因此效果不好;
- 很多时候我们想打印网页的某一部分(而不是整个网页),因此可定制性较差。
因此我们需要结合js来实现以上需求。
二、用js
1、js + 浏览器打印功能
(1)打印整个页面:
window.print()
(2)打印页面中的指定元素:
一般来说,我们会用CSS将不需要打印的元素隐藏掉,这样body中实际上就只剩我们希望打印的页面部分:
@media print {
body * {
visibility: hidden;
}
#printArea, #printArea * {
visibility: visible;
}
#printArea {
position: fixed;
left: 0;
top: 0;
right:0;
bottom:0;
}
}
再调用window.print()
即可。
但是测试后发现,这种方法还是存在问题:如果要打印的元素较长(大于一屏),大于一屏的部分不会被打印。
我们再换一种思路:
//打印区域的内容
var printContents = document.getElementById('printArea').innerHTML
//页面的所有内容
var originalContents = document.body.innerHTML
document.body.innerHTML = printContents
window.print()
document.body.innerHTML = originalContents
//直接恢复页面可能会造成页面“假死”,可通过重新加载的方式恢复
window.location.reload()
打印的时候将要打印的部分设置为当前页面的全部内容,打印完之后又将页面恢复。
分别打印一个页面上的多个区域:
//第一个打印区域的内容
var printContents1 = document.getElementById('printArea1').innerHTML
//第二个打印区域的的内容
var printContents2 = document.getElementById('printArea2').innerHTML
//页面的所有内容
var originalContents = document.body.innerHTML
//打印第一个区域
document.body.innerHTML = printContents1
window.print()
//打印第二个区域
document.body.innerHTML = printContents2
window.print()
//恢复页面
document.body.innerHTML = originalContents
//直接恢复页面可能会造成页面“假死”,可通过重新加载的方式恢复
window.location.reload()
这种方法可以使得每次打印时要打印的内容都在页面开头,并且对于超过一屏的打印也正确。
存在的问题:
打印完之后需手动将页面恢复至原来的html结构,恢复之后发现页面“假死”,无法再进行其他交互,因此我调用了window.location.reload()
将页面刷新。但是这样做实际上是没有保存页面目前的一些数据和交互信息,因此用户体验也不好。
如果不希望牺牲用户体验,可以将要打印的内容写入新的浏览器窗口并打开,打印完之后关闭该新窗口:
var printContents = document.getElementById('printArea').innerHTML
//打开打印窗口
var newWin = window.open()
newWin.document.write(printContents)
newWin.print()
//关闭打印窗口
newWin.close()
我们会发现,使用这种方法的话,打印内容的css样式实际上是失效的,因为在打印窗口页面并没有引入相关的css,所以我们需要手动引入:
var printContents = document.getElementById('printArea').innerHTML
var newWin = window.open()
//引入css
var printCommonStyle=`
<style>
*{
box-sizing: border-box;
}
.text-center{
text-align: center;
}
a,a:link,a:visited,a:hover,a:active{
color: #000;
text-decoration: none;
}
/*设置打印时的table样式*/
table{
border-spacing: 0px;
border-bottom: 1px solid;
}
th,td {
padding: 4px 4px;
word-break: break-all;
border-top: 1px solid;
border-left: 1px solid;
}
tr>th:last-child,
tr>td:last-child {
border-right: 1px solid;
}
</style>
`
newWin.document.write( printCommonStyle+printContents )
newWin.print()
newWin.close()
这样也能实现一些场景下的打印要求(比如表格、文字等)。
2、js插件实现打印功能
下载为图片:html2canvas + a标签download属性
打印为PDF:html2canvas + jsPDF
首先我们看看这几个js的使用方法:
1)html2canvas
步骤:引入html2canvas.js,在Promise.then中获取转换好的canvas:
//通过html2canvas把html渲染成canvas,然后获取图片数据
html2canvas(document.getElementById('showPart')).then(function(canvas) {
//将canvas转成PDF或者下载图片
//.........................................
});
2)下载图片
其实要用到a标签的download特性:
//通过html2canvas把html渲染成canvas,然后获取图片数据
html2canvas(document.getElementById('showPart')).then(function(canvas) {
//下载图片
var aElement=document.createElement('a');
aElement.setAttribute('href',imageData); //重点看这里
aElement.setAttribute('download','test.png'); //重点看这里
document.body.insertAdjacentElement('beforeend',aElement)
aElement.click();
});
3)jsPDF
(1)文字打印成PDF:
var doc=new jsPDF()
doc.text('hello world!',10,10)
doc.save('a4.pdf')
(2)图片打印成PDF:
我们看看官网的例子:
注意:jsPDF需要的是data URL的图片格式。
借助于官网的例子,我们进行一下模拟,代码:
var imageData='data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAADAAAAAwCAYAAABXAvmHAAAABGdBTUEAALGPC/xhBQAAACBjSFJNAAB6JgAAgIQAAPoAAACA6AAAdTAAAOpgAAA6mAAAF3CculE8AAAABmJLR0QA/wD/AP+gvaeTAAAAB3RJTUUH3gwKEAonDW4kCwAAAodJREFUaN7t2LtrFUEYxuFHTWGCBrzFS6WilcGgnSAoGLX2UgiChSGF2NtYiEII+C8E0oid0VqxUBDsFAUrRS0UUYkBlaNgvBQzMcclObvubLIp9q12l/lm3t/M7M73LY0aNWpUp5ZV2dnmC08LtXt3dffSA8iYX4F16I33nzGJn1VDJANkjPdgEMcxgLXx+Sc8wU3cRasqkCSAjPmtuIyTEWQutXADl/C6CoiuJPxZbcMYDuW068EZbMFwO0RZLS8b2Db7PcLM55lv1yCuxNjCL3+lABkzJ0rEnYixSUoF6NJ5z3dST4xN2sapAGuEr01ZDcQ+agNYm2hgjdlPbS0AVRyESX2kAkxiKiF+KvZRG8AUHifEP06cgGSAaSE9aJWIbcXY6ToBCLnNRIm4iRibpKpyoe0Yx4GCofdxFi9Jy4WqWAHRyBCu6bydWrHN0Iz5VC1UOn0Me82eEVN4hFuWUjo9DwSzBc3KeP9dpqCpwnxlADkw/6jKcrIygA3D90rFfRw7WA9AB8PLhG3TjdVYFZ9/xRd8E7bT76qA/gtgHuN92ClklruwAxuj+fZ34Cve4wWeCTXyc3xIASkEMIfxddiHo9gvlJS9/zEhv4U/Fa/wALfxUCYvKgKSO2DGfJ9QhJzGHmGrVKFvQl50XSj6/65KHkRHgIz5I7gozHhVB2BWv4QVGcGdIhDzArSZXy4c+yPCCiyGPsTJGo9Q80IUmcmjGF1E8+JYozic1zAPoBvnsH4Rzc9oPc7Lec/yADahvwbzM+qPHkoDdKnu710Z5Y6/UF+TRVMRgDohc8fO2x7TeBuvf9Vg/q2cmjkP4A1OFWi3UJqOHkoD/BDylUaNGjVqNKf+AN+rkUhAKMRGAAAAJXRFWHRkYXRlOmNyZWF0ZQAyMDE3LTExLTEyVDExOjAyOjI0KzA4OjAwmpHX/gAAACV0RVh0ZGF0ZTptb2RpZnkAMjAxNC0xMi0xMFQxNjoxMDozOSswODowMOM3CTMAAABNdEVYdHNvZnR3YXJlAEltYWdlTWFnaWNrIDcuMC4xLTYgUTE2IHg4Nl82NCAyMDE2LTA5LTE3IGh0dHA6Ly93d3cuaW1hZ2VtYWdpY2sub3Jn3dmlTgAAABh0RVh0VGh1bWI6OkRvY3VtZW50OjpQYWdlcwAxp/+7LwAAABh0RVh0VGh1bWI6OkltYWdlOjpIZWlnaHQAMTI4Q3xBgAAAABd0RVh0VGh1bWI6OkltYWdlOjpXaWR0aAAxMjjQjRHdAAAAGXRFWHRUaHVtYjo6TWltZXR5cGUAaW1hZ2UvcG5nP7JWTgAAABd0RVh0VGh1bWI6Ok1UaW1lADE0MTgxOTkwMzk6CQwlAAAAEnRFWHRUaHVtYjo6U2l6ZQAxLjA3S0Lad01iAAAAX3RFWHRUaHVtYjo6VVJJAGZpbGU6Ly8vaG9tZS93d3dyb290L3NpdGUvd3d3LmVhc3lpY29uLm5ldC9jZG4taW1nLmVhc3lpY29uLmNuL3NyYy8xMTgyMS8xMTgyMTEyLnBuZ9ePzJoAAAAASUVORK5CYII='
var doc = new jsPDF()
doc.addImage(imageData,'png',0,0,100,100)
doc.save('resume.pdf')
4)得到data url
其实这个功能只是调用一个toDataURL的API而已,所以不需要用什么插件,直接调用就可以了:
//默认的转换类型:"image/png"
var imageData=canvas.toDataURL()
知道上述几个API和插件的基本用法后,我们来将它们组合起来:先使用html2canvas把网页转成canvas,再使用toDataURL()方法把canvas转成data url,最后用jsPDF把data url转成PDF,简直完美!
完整代码如下:
html2canvas(document.getElementById('showPart')).then(function(canvas) {
//通过html2canvas把html渲染成canvas
document.body.appendChild(canvas);
//canvas转成dataurl
var imageData=canvas.toDataURL()
//dataurl转成PDF
var doc = new jsPDF('','pt','a4')
doc.addImage(imageData,'png', 0, 0, 595, 595/canvas.width * canvas.height) //使图片占满整张A4纸
doc.save('test.pdf')
});
------补充-------
测试发现,以上代码得到的结果存在两个严重问题:
- 当图片比较长时,超出A4纸长度的那部分图片会被截掉
- 最终得到的PDF不清晰,很模糊
我翻看了很多资料,试了很多方法,分页解决了,但是得到的PDF还是很模糊。目前还未解决,先在这里占个坑吧,等到解决了再来整理。
总之,目前还没找到完美的方案来解决打印的问题,如果您有什么解决方案,可以多多交流哦~~
参考:
1、Print <div id=“printarea”></div> only?
2、Javascript 将html转成pdf,下载,支持多页哦
3、html导出pdf文件
4、问:html5 canvas绘制图片模糊的问题