浏览器打印的两种实践方案
1.浏览器自身打印
- 使用
window.print()
调起浏览器自带的打印预览弹框打印 - 默认会打印
body
里面所有内容
js
const handlePrintPdf = () => {
window.print();
}
如果想要局部打印可以使用 CSS
媒体查询,甚至还可以自定义打印时的样式,这些样式只会在打印的时候生效
```html
```
可使用媒体查询在打印的时候去除页眉页脚,设置打印布局等操作
```css @media print { @page { / 去除页眉 / / margin-top: 0; / / 去除页脚 / / margin-bottom: 0; / / 去掉页眉和页脚 / margin: 0; / 纵向 / size: portrait; / 横向 A4 / size:A4 landscape; }
body {
margin: 1cm;
}
}
```
如果内容固定且具有分页,浏览器自身分页可能会和预想中情况不一样,我们可以使用 CSS
进行强制分页
css
@media print {
/* 在main元素前始终插入分页符,强制使其分到下一页 */
main {
page-break-before: always;
}
/* 在footer元素后始终插入分页符 */
footer {
page-break-after: always;
}
}
打印部分区域的内容还可以这样做
动态创建一个不可见的 iframe
, 将需要打印的 dom
节点插入 iframe
内,并调用 iframe
的 print
方法
js
printBtn.addEventListener("click", () => {
const printContentHtml = document.getElementById("print").innerHTML;
const iframe = document.createElement("iframe");
iframe.setAttribute(
"style",
"position:absolute;width:0px;height:0px;left:-500px;top:-500px;"
);
document.body.appendChild(iframe);
iframe.contentDocument.write(printContentHtml);
iframe.contentDocument.close();
iframe.contentWindow.print();
document.body.removeChild(iframe);
})
或者在新打开的页面打印
js
printBtn.addEventListener("click", () => {
const printContentHtml = document.getElementById("print").innerHTML;
const printPage = window.open();
printPage.document.write(printContentHtml);
printPage.document.close();
printPage.print();
printPage.close();
})
当然我们也可以这样,先获取需要打印的 dom
节点,替换当前 body
下的节点。完成打印后恢复 body
下的节点
```html
段落1
段落2
段落3
```
这样的话打印的时候页面原有元素会丢失,并且这时可能图片也未加载完成,很多元素宽度也会有问题要调整,我们可以更进一步封装方便修改,整体代码如下:
```html
段落1~
段落2~
段落3~
```
很可惜此类方案和很多第三方库会有元素丢失和样式错乱的情况,不尽如人意
此时一个方案浮出水面
2.html2canvas和jspdf
- 简单说就是将网页通过
html2canvas
(dom-to-image
也可以) 转换为图片,再由jspdf
生成该图片的pdf
- 此方案也会有一些问题,例如
iframe
嵌套(项目中富文本编辑器会用到)的内容会无法打印,无法复制pdf
上的文字等内容,而且清晰度可能会存在问题 - 图片输出
pdf
的时候根据宽高裁切,分页的时候可能会有内容生生的被截断,需要根据不同项目特殊处理(逻辑复杂)
先来说说这两者基本使用
安装:
shell
yarn add html2canvas jspdf
使用 html2canvas
对页面截图
js
// allowTaint 是否允许跨域加载图片
// useCORS 是否使用CORS从服务器加载图片
// scale 渲染像素比率,默认当前设备像素比
printBtn.addEventListener("click", () => {
html2Canvas(wrap, { allowTaint: true, useCORS: true, scale: 2 }).then(
(canvas) => {
document.body.appendChild(canvas);
// 转换canvase为base64图片,第二个参数1代表图片质量(0-1)
canvas.toDataURL("image/jpeg", 1);
}
);
});
此方法无法打印 iframe
里面的内容,于是我遍历整个 iframe
的 dom
替换,此外我们还要书写逻辑手动对图片指定宽高进行截图分页,此部分可以将其封装下方便复用,具体方案如下
```html
```
注意打印完之后最好在顶层组件封装个 reload
方法关联 v-if
通过 provide
提供下去,组件调用下重新渲染一遍(因为之前我们替换了页面元素,不刷新还原可能会有问题)
js
reload() {
this.isRouteAlive = false;
this.$nextTick(() => {
this.isRouteAlive = true;
});
},
最后这个截断问题比较棘手,我们可以这样解决
- 设置对应转换后的 canvas 为白色背景
- 转换图片后获取对应截断处的图片像素
- 从截断处一行行往上扫描像素点颜色
- 碰到这一行是全白的,代表从这里截断,将这个高度往下的内容放到下一页
具体代码如下!
```html
```