最近有个页面打印图片的小需求。就是系统界面展示有一些证件照片,我们希望可以点击图片旁边的打印小按钮,就可以将这张图片直接打印到A4纸张上,例如下图效果:

其实浏览器 window 对象提供了 print 方法,就可以对整个页面进行打印。只需要点击按钮执行以下方法即可。
调用此方法,会打印出整个 html 里的内容,即 document 对象下所有的页面节点。而我们需要的是只打印页面的某个元素部分,即只打印图片。
很遗憾,浏览器在 具体的dom 节点上并没有部署 print 方法,不过我们可以转变个思路,我们可以将需要打印的元素提取出来,同时构造一个新的window对象,将提取出来的元素插入到这个window对象下,再调用打印即可。
- <button @click="print">打印</button>
- <div id="box">
- <img src="/test.jpg"/>
- </div>
例如我们只需要打印id="box"下的 img
- print(){
- const el = document.querySelector("#box")
- var newWindow=window.open("打印窗口","_blank");
- var docStr = el.innerHTML;
- newWindow.document.write(docStr);
- newWindow.document.close();
- newWindow.print();
- newWindow.close();
- },
通过 window.open 方式返回一个新的 window 对象,再调用 document.write 写入我们获取到指定节点,再打印即可。
这种方式有点不好的就是需要重新开一个 window ,并且设置一些打印的样式会比较麻烦。所以不推荐。
我查阅了一些知名的打印插件,都是采用的 iframe
来构造页面来实现局部打印的。iframe 有个属性 srcdoc
可以渲染指定的html内容
- <iframe srcdoc="<p>Hello world!</p>"></iframe>
以往我们都是通过src来加载一个指定的页面地址,这里通过 srcdoc 来渲染指定的html内容。下面实现一个最简单的点击按钮打印图片功能:
- // 打印
- function btnClick(){
- const iframe = document.createElement('iframe')
- // 视觉上隐藏 iframe
- iframe.style.height = 0
- iframe.style.visibility = 'hidden'
- iframe.style.width = 0
- const str =
- `<html>
- <style media='print'>
- @page{size:A4 landscape};margin:0mm;padding:0}
- </style>
- <body>
- <div id="box"></div>
- </body>
- </html>
- `
- iframe.setAttribute('srcdoc', str);
- document.body.appendChild(iframe);
- // 一定要加载完成后执行
- iframe.addEventListener("load",()=>{
- const image = document.querySelector('img').cloneNode();
- image.style.display = 'block'
- const box = iframe.contentDocument.querySelector('#box');
- box.appendChild(image);
- // 一定要图片加载完再打印
- image.addEventListener('load', function () {
- // 打印
- iframe.contentWindow.print();
- });
- })
- iframe.contentWindow.addEventListener('afterprint', function () {
- iframe.parentNode.removeChild(iframe);
- });
- }
对于打印的样式设置,可以通过在style标签上添加media=print
来设置
- <style media='print'>
- @page{size:A4 landscape};margin:0mm;padding:0}
- </style>
上述就指定了打印机默认格式为A4纸张 横向打印 ,margin设置成0毫米是为了保证不出现页眉页脚。
基础功能的打印实现了,可是为了让打印体验更好,产品经理又提出了需求点:
横图效果:

纵图效果:

实现思路: 由于要保证纸张边缘留有一定的空白区域,这个时也可以使用 margin 来实现。
- <style media='print'>
- @page{size:A4 landscape;margin:10mm;}
- </style>
但是不将 margin 设置成 0 的话,又会默认出现页眉页脚。这显然是矛盾的。这个时候我想到了一个好的思路,就是将图片放置到一个 div 容器里,这个 div 宽高设置成 A4 一样的大小。同时将div里面的图片通过 flex 布局来实现上下左右都居中。然后打印区域设置成这个容器就可以了。

由于 div 和 A4 纸张一样大,所以 @page 里可以设置成 margin:0mm 来规避页眉页脚的出现。然后里面的图片需要居中
- // 获取图片宽高比
- const rate = owidth/oheight
- // 横图的话容器宽度就是A4的高度,即29.7cm,纵图的话宽度就是21cm,由于刚好设置成21cm会溢出,多出一张纸,原因未明,所以我设置成20.9
- const boxWidthCM = `${rate >1 ? 29.7 : 20.9}cm`
- // 容器高度
- const boxHeightCM = `${rate >1 ? 20.9 : 29.7}cm`
-
- const str =
- `<html>
- <style media='print'>
- @page{size:A4 ${rate>1 ? 'landscape':'portrait'};margin:0mm;padding:0}
- </style>
- <style>
- *{padding:0;margin:0}
- body{height:100%}
- #box{
- width:${boxWidthCM};
- height:${boxHeightCM};
- display:flex;
- align-items:center;
- justify-content:center;
- }
- </style>
- <body>
- <div id="box"></div>
- </body>
- </html>`
- iframe.setAttribute('srcdoc', str);
居中问题解决了,接下来就是解决图片尽可能铺满纸张问题。这个时候我们需要结合容器大小以及图片宽高比来手动计算图片宽高,算法如下:
- let imgW = null;
- let imgH = null;
- if(rate > 1){ // 横图
- if(rate>1.414){
- imgW = 29.7
- imgH = 29.7/rate
- } else {
- imgH = 20.9
- imgW = 20.9*rate
- }
- } else {
- if(rate>(1/1.414)){
- imgW = 20.9
- imgH = 20.9/rate
- } else {
- imgH = 29.7
- imgW = 29.7*rate
- }
- }
-
- // 预留1cm边距
- imgW = imgW - 1
- imgH = imgW/rate
- iframe.addEventListener("load",()=>{
- const image = document.createElement("img")
- image.style.width = item.width
- image.style.height = item.height
- image.style.display = 'block'
- image.src = item.newUrl || item.url || item.original_content_url
- image.style.width = `${imgW}cm`
- image.style.height = `${imgH}cm`
- const box = iframe.contentDocument.querySelector('#box');
- box.appendChild(image);
- image.addEventListener('load', function () {
- iframe.contentWindow.print();
- });
- })
完整代码:
- print(item){
- const { owidth,oheight,height } = item
- const rate = owidth/oheight
- const imgHeight = height.replace("px","")
- const iframe = document.createElement('iframe')
- iframe.style.height = 0
- iframe.style.visibility = 'hidden'
- iframe.style.width = 0
- const boxWidthCM = `${rate >1 ? 29.7 : 20.9}cm`
- const boxHeightCM = `${rate >1 ? 20.9 : 29.7}cm`
- let imgW = null;
- let imgH = null;
- if(rate > 1){ // 横图
- if(rate>1.414){
- imgW = 29.7
- imgH = 29.7/rate
- } else {
- imgH = 20.9
- imgW = 20.9*rate
- }
- } else {
- if(rate>(1/1.414)){
- imgW = 20.9
- imgH = 20.9/rate
- } else {
- imgH = 29.7
- imgW = 29.7*rate
- }
- }
-
- // 预留1cm边距
- imgW = imgW - 1
- imgH = imgW/rate
-
- const str =
- `<html>
- <style media='print'>
- @page{size:A4 ${rate>1 ? 'landscape':'portrait'};margin:0mm;padding:0}
- </style>
- <style>
- *{padding:0;margin:0}
- body{height:100%}
- #box{
- width:${boxWidthCM};
- height:${boxHeightCM};
- display:flex;
- align-items:center;
- justify-content:center;
- }
- </style>
- <body>
- <div id="box"></div>
- </body>
- </html>`
- iframe.setAttribute('srcdoc', str);
- document.body.appendChild(iframe);
- iframe.addEventListener("load",()=>{
- const image = document.createElement("img")
- image.style.width = item.width
- image.style.height = item.height
- image.style.display = 'block'
- image.src = item.newUrl || item.url || item.original_content_url
- image.style.width = `${imgW}cm`
- image.style.height = `${imgH}cm`
- const box = iframe.contentDocument.querySelector('#box');
- box.appendChild(image);
- image.addEventListener('load', function () {
- iframe.contentWindow.print();
- });
- })
- iframe.contentWindow.addEventListener('afterprint', function () {
- iframe.parentNode.removeChild(iframe);
- });
- }
总结
到此这篇关于JavaScript图片打印方案的文章就介绍到这了,更多相关JavaScript 图片打印内容请搜索w3xue以前的文章或继续浏览下面的相关文章希望大家以后多多支持w3xue!