把网页下载为图片或打印成PDF

最近遇到了需要把网页下载为图片和打印为PDF的需求,特此整理。

一、借助浏览器

一般浏览器都自带PDF打印功能:

至于保存为图片,有很多浏览器插件(比如screenshot、花瓣)等,都可以将网页保存为图片。

缺点:

  1. 网页中的图标、背景等可能无法打印成PDF,因此效果不好;
  2. 很多时候我们想打印网页的某一部分(而不是整个网页),因此可定制性较差。

因此我们需要结合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')
});

------补充-------

测试发现,以上代码得到的结果存在两个严重问题:

  1. 当图片比较长时,超出A4纸长度的那部分图片会被截掉
  2. 最终得到的PDF不清晰,很模糊

我翻看了很多资料,试了很多方法,分页解决了,但是得到的PDF还是很模糊。目前还未解决,先在这里占个坑吧,等到解决了再来整理。

总之,目前还没找到完美的方案来解决打印的问题,如果您有什么解决方案,可以多多交流哦~~

参考:
1、Print <div id=“printarea”></div> only?
2、Javascript 将html转成pdf,下载,支持多页哦
3、html导出pdf文件
4、问:html5 canvas绘制图片模糊的问题

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 202,802评论 5 476
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 85,109评论 2 379
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 149,683评论 0 335
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,458评论 1 273
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,452评论 5 364
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,505评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 37,901评论 3 395
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,550评论 0 256
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,763评论 1 296
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,556评论 2 319
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,629评论 1 329
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,330评论 4 318
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,898评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,897评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,140评论 1 259
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 42,807评论 2 349
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,339评论 2 342

推荐阅读更多精彩内容

  • 用到的组件 1、通过CocoaPods安装 2、第三方类库安装 3、第三方服务 友盟社会化分享组件 友盟用户反馈 ...
    SunnyLeong阅读 14,598评论 1 180
  • 发现 关注 消息 iOS 第三方库、插件、知名博客总结 作者大灰狼的小绵羊哥哥关注 2017.06.26 09:4...
    肇东周阅读 12,016评论 4 62
  • 今天一个人去散步,突然发现自己很孤单,以前一直以来都有很多很多的朋友在身边,当他人约我一起吃饭也好,散步也好,怎样...
    高藝菲Sophia阅读 292评论 2 1
  • 总想把你珍藏 只因你纯真模样 总想留住时光 深情依然 月夜的故事 吐放玫瑰花香 萦绕成茧 你在那里 牵动思念的线 ...
    律墨阅读 362评论 9 5
  • 姓名:邵逸轩 公司:宁波禾隆新材料股份有限公司 六项精进第340期努力一组学员 【日精进打卡第13天】 【知~学习...
    邵逸轩阅读 157评论 0 0