首頁 > 軟體

JavaScript圖片列印方案範例詳解

2023-03-19 06:01:28

最近有個頁面列印圖片的小需求。就是系統介面展示有一些證件照片,我們希望可以點選圖片旁邊的列印小按鈕,就可以將這張圖片直接列印到A4紙張上,例如下圖效果:

其實瀏覽器 window 物件提供了 print 方法,就可以對整個頁面進行列印。只需要點選按鈕執行以下方法即可。

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毫米是為了保證不出現頁首頁尾。

基礎功能的列印實現了,可是為了讓列印體驗更好,產品經理又提出了需求點:

  • 當圖片是橫圖時,即寬度大於高度的圖片時,需要將A4紙張橫向列印,然後圖片在A4裡面上下左右都居中。同時要將這張圖片儘可能地鋪滿A4紙張,也不能改變圖片的寬高比(即不變形)。

  • 當圖片是縱圖時,即寬度小於高度的圖片時,需要將A4紙張縱向列印,然後圖片在A4裡面上下左右都居中。同時要將這張圖片儘可能地鋪滿A4紙張,也不能改變圖片的寬高比(即不變形)。

  • 圖片不要緊挨著紙張邊緣,留出一定邊距。

橫圖效果:

縱圖效果:

實現思路: 由於要保證紙張邊緣留有一定的空白區域,這個時也可以使用 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 圖片列印內容請搜尋it145.com以前的文章或繼續瀏覽下面的相關文章希望大家以後多多支援it145.com!


IT145.com E-mail:sddin#qq.com