项目中想要将侧边弹窗里的内容导出成 PDF. 纯前端手段是否可以完成呢?
所有的 demo 都用最简便的代码实现, 可以直接控制台执行;
欢迎评论更多思路
方案概要
css 媒体查询
iframe 打开 DOM 元素
将 body 重置为目标 dom 元素, 打印后还原 body
三方库 jsPDF
三方库 jsPDF + 三方库 html2canvas
原理简述
1. css 媒体查询 print, 可以在打印时隐藏不相关的元素, 将要打印的局部占满全屏, z-index 设置极高
2. window.print 打印可另存为 pdf
1. 创建一个空的 iframe 我们可以完全控制 window 对象和 dom 节点
2. 往 iframe 中插入想要打印的 html 模板
3. 往 iframe 中插入想要打印的 css 样式
4. 触发 iframe 的 window.print 另存为 pdf
1. 将整个 body 的 dom 节点缓存下来
2. 将 body 设置为打印的目标元素
3. 触发 window.print 另存为 pdf
4. 还原 body 的 dom 节点
1. 可以创建 pdf 文件, 然后逐个元素添加至 pdf 文件中;
2. 也可以将 dom 节点转换为 pdf
1. html2canvas 将 dom 节点转换为图片
2. jsPDF 将图片转换为 pdf 文件
优点
- 常规打印样式设置, 可以用于网页 pdf 导出; 也可以稍加调整用于网页局部 pdf 导出;
- 改动难度小, 不需要编写 JS 逻辑
- 可以将任意内容导出成 pdf 文档, 而不必是网页上的内容
- 改动非常小, 实现非常快速
- 可以完全掌控 pdf 文件的构造, 逐个段落输出
- 改动非常小, 实现非常快速
缺点
- 没有明显缺点
- 没有明显缺点
- 会丢失 DOM 节点绑点的事件, 大部分场景这思路就不可用
- 打印过程中, 网页内容被改变, 略微影响用户体验
- 目标元素的样式如果和 DOM 层级结构相关将会丢失
- 需要前端引入字体库, 否则就是乱码 (20M 左右),
- 直接将 dom 节点转 pdf 效果较差, 丢失大量样式设置
- 丢失 pdf 文档的文本内容, 和直接导出图片没有区别
- html2canvas 库的 bug, 会造成部分样式错误渲染, 比如阴影;
demo
demo 1
demo 2
demo 3
demo 4
demo 5
所有的 demo 都用最简便的代码实现, 可以直接控制台执行;
示例网站: baidu.com
第一步: 在 element 中新增 <style>
标签, 内容如下
xml
复制代码
`<style>
.mnav.c-font-normal.c-color-t {
color: red;
}
@media print {
.mnav.c-font-normal.c-color-t {
color: green !important;
}
}
</style>`
头部超链接颜色变成了红色
第二步: 调试控制台运行 window.print()
, 打印的 PDF 中头部超链接颜色显示为绿色
示例网站: baidu.com
调试控制台直接执行如下代码:
ini
复制代码
`function printElement(e) {
var ifr = document.createElement('iframe');
ifr.style = 'height: 0px; width: 0px; position: absolute'
document.body.appendChild(ifr);
ifr.contentDocument.body.appendChild(e.cloneNode(true))
ifr.contentWindow.print();
ifr.parentElement.removeChild(ifr);
}
var style = document.createElement('style')
style.innerText = '* { color: red; }'
var img = document.createElement('img')
img.height = 300;
img.width = 300;
img.src = 'https://www.baidu.com/img/PCtm_d9c8750bed0b3c7d089fa7d55720d6cf.png'
var div = document.createElement('div')
div.innerText = "这是百度网站"
div.appendChild(style)
div.appendChild(img)
printElement(div)`
现在我们就是想打印什么打印什么
示例网站: baidu.com
第一步: 调试控制台 - element 页签, 选中某一个元素
arduino
复制代码
`// $0 是 chrome 调试工具选中的元素
// 没有使用过的话, 可以在 Element 页签点中任意一个 HTML 标签
// 然后控制台打印看看 $0 是啥`
第二步: 调试控制台运行如下代码
方案特别简单, 就不解释源码了
ini
复制代码
`// $0 是 chrome 调试工具选中的元素
let storeNodeHtml = document.body.innerHTML
let printNode = $0.cloneNode(true)
document.body.innerHTML = ''
document.body.innerHTML = printNode.innerHTML
window.print()
document.body.innerHTML = storeNodeHtml`
打印整个网页还好, 打印局部可能会丢失样式;毕竟把某个元素直接丢进 <body> 元素, 丢失了层级结构, css 选择器将会失效; 自己试试就知道了;
示例网站: google.com
第一步: 运行代码加载三方库
ini
复制代码
`// 控制台执行第一段
var jspdfScript = document.createElement('script')
jspdfScript.src="https://unpkg.com/jspdf@latest/dist/jspdf.umd.min.js";
document.body.append(jspdfScript)
var html2canvasScript = document.createElement('script')
html2canvasScript.src="https://html2canvas.hertzen.com/dist/html2canvas.min.js";
document.body.append(html2canvasScript)`
第二步: 调试控制台 - element 页签, 选中某一个元素, demo 直接选取 <body>
第三步: 调试控制台 - 运行如下代码
javascript
复制代码
`// 等 js 加载完成, 控制台执行第二段
// $0 是调试工具选中的元素, 没有使用过的话, 可以直接控制台打印看看 $0 是啥
window.jsPDF = jspdf.jsPDF
var doc = new jsPDF()
doc.html($0, {
callback: function(doc) {
doc.save('sample-document.pdf');
},
x: 1,
y: 1,
width: 170,
windowWidth: 1650
});`
图片变形, 中文乱码....懒得解决
示例网站: google.com
第一步: 运行代码加载三方库
ini
复制代码
`// 控制台执行第一段
var jspdfScript = document.createElement('script')
jspdfScript.src="https://unpkg.com/jspdf@latest/dist/jspdf.umd.min.js";
document.body.append(jspdfScript)
var html2canvasScript = document.createElement('script')
html2canvasScript.src="https://html2canvas.hertzen.com/dist/html2canvas.min.js";
document.body.append(html2canvasScript)`
第二步: 调试控制台 - element 页签, 选中某一个元素, demo 直接选取 <body>
第三步: 调试控制台 - 运行如下代码
arduino
复制代码
`// 等 js 加载完成, 控制台执行第二段
// $0 是调试工具选中的元素, 没有使用过的话, 可以直接控制台打印看看 $0 是啥
window.jsPDF = jspdf.jsPDF
html2canvas($0).then(function(canvas) {
var max = { height:300 - 40 2, width: 210 - 15 2 }
var doc = new jsPDF("p", "mm", 'a2')
var height=canvas.height
var width=canvas.width
var ratio=canvas.height / canvas.width;
if(height > max.height){// 先调整高
height = max.height;
width = height * ( 1 / ratio);
}
if (width > max.width) {// 再调整宽
width = max.width
height = width * ratio
}
// 最后宽高都是合适的
doc.addImage(canvas, 'PNG', 15, 40, width, height)
doc.save('sample-document.pdf')
});`
效果明显比 demo 4 要好, 并且没有乱码的问题,但这是一整张图片, 丢失了 pdf 内容可搜索, 可复制的特性;
将自定义长列表导出, 不需要操作按钮, 表格所有选项默认展开, 去掉装饰性元素.
方案概要
css 媒体查询
iframe 打开 DOM 元素
将 body 重置为目标 dom 元素, 打印后还原 body
三方库 jsPDF
三方库 jsPDF + 三方库 html2canvas
结论
不采纳
作为华为云官网微服务, 大量框架相关的元素和外部样式
采纳
需要重新编写一套模板和 css, 成本略略高, 但是完全可控;
备选
不需要重新编写全套样式, 在 angular 样式模块的背景下也不会丢失全局的样式; 会丢失所有 DOM 元素的监听事件...要全局重新渲染一遍;
不采纳
字体库 20M, 前端加载用户体验非常差
不采纳
丢失了 pdf 文本属性, 不可搜索, 不可复制粘贴, 不满足业务要求
原网址: 访问
创建于: 2023-10-10 09:37:26
目录: default
标签: 无
未标明原创文章均为采集,版权归作者所有,转载无需和我联系,请注明原出处,南摩阿彌陀佛,知识,不只知道,要得到
最新评论