更新時間:2023年07月17日 09:05:56 作者:jecyu
這篇文章主要為大家詳細(xì)介紹了如何使用html2Canvas打印高清PDF的效果,文中的示例代碼講解詳細(xì),具有一定的參考價值,需要的可以了解一下
+
目錄
1. 前言
最近我需要將網(wǎng)頁的DOM輸出為PDF文件,我使用的技術(shù)是html2Canvas和jsPDF。具體流程是,首先使用html2Canvas將DOM轉(zhuǎn)化為圖片,然后將圖片添加到j(luò)sPDF中進(jìn)行輸出。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
const pdf = new jsPDF({
unit: 'pt',
format: 'a4',
orientation: 'p',
});
const canvas = await html2canvas(element,
{
onrendered: function (canvas) {
document.body.appendChild(canvas);
}
}
);
const canvasData = canvas.toDataURL('image/jpeg', 1.0);
pdf.addImage(canvasData, 10, 10);
pdf.save('jecyu.pdf');
遇到了圖片導(dǎo)出模糊的問題,解決思路是:
1.先html2canvas 轉(zhuǎn)成高清圖片,然后再傳一個 scale 配置:
1
scale: window\.devicePixelRatio \* 3, // 增加清晰度
2.為了確保圖片打印時不會變形,需要按照 PDF 文件的寬高比例進(jìn)行縮放,使其與 A4 紙張的寬度一致。因?yàn)?A4 紙張采用縱向打印方式,所以以寬度為基準(zhǔn)進(jìn)行縮放。
1
2
3
4
5
6
7
8
// 獲取canavs轉(zhuǎn)化后的寬度
const canvasWidth = canvas.width;
// 獲取canvas轉(zhuǎn)化后的高度
const canvasHeight = canvas.height;
// 高度轉(zhuǎn)化為PDF的高度 const height = (width / canvasWidth) \* canvasHeight;
// 1 比 1 進(jìn)行縮放
pdf.addImage(data, 'JPEG', 0, 0, width, height);
pdf.save('jecyu.pdf');
要想了解為什么這樣設(shè)置打印出來的圖片變得更加清晰,需要先了解一些有關(guān)圖像的概念。
2. 一些概念
2.1 英寸
英寸是用來描述屏幕物理大小的單位,以對角線長度為度量標(biāo)準(zhǔn)。常見的例子有電腦顯示器的17英寸或22英寸,手機(jī)顯示器的4.8英寸或5.7英寸等。厘米和英寸的換算是1英寸等于2.54厘米。
2.2 像素
像素是圖像顯示的基本單元,無法再分割。它是由單一顏色的小方格組成的。每個點(diǎn)陣圖都由若干像素組成,這些小方格的顏色和位置決定了圖像的樣子。
圖片、電子屏幕和打印機(jī)打印的紙張都是由許多特定顏色和位置的小方塊拼接而成的。一個像素通常被視為圖像的最小完整樣本,但它的定義和上下文有關(guān)。例如,我們可以在可見像素(打印出來的頁面圖像)、屏幕上的像素或數(shù)字相機(jī)的感光元素中使用像素。根據(jù)上下文,可以使用更精確的同義詞,如像素、采樣點(diǎn)、點(diǎn)或斑點(diǎn)。
2.3 PPI 與 DPI
PPI (Pixel Per Inch):每英寸包括的像素數(shù),用來描述屏幕的像素密度。
DPI(Dot Per Inch):即每英寸包括的點(diǎn)數(shù)。
在這里,點(diǎn)是一個抽象的單位,可以是屏幕像素點(diǎn)、圖片像素點(diǎn),也可以是打印的墨點(diǎn)。在描述圖片和屏幕時,通常會使用DPI,這等同于PPI。DPI最常用于描述打印機(jī),表示每英寸打印的點(diǎn)數(shù)。一張圖片在屏幕上顯示時,像素點(diǎn)是規(guī)則排列的,每個像素點(diǎn)都有特定的位置和顏色。當(dāng)使用打印機(jī)打印時,打印機(jī)可能不會規(guī)則地打印這些點(diǎn),而是使用打印點(diǎn)來呈現(xiàn)圖像,這些打印點(diǎn)之間會有一定的空隙,這就是DPI所描述的:打印點(diǎn)的密度。
在這張圖片中,我們可以清晰地看到打印機(jī)是如何使用墨點(diǎn)打印圖像的。打印機(jī)的DPI越高,打印出的圖像就越精細(xì),但同時也會消耗更多的墨點(diǎn)和時間。
2.4 設(shè)備像素
設(shè)備像素(物理像素)dp:device pixels,顯示屏就是由一個個物理像素點(diǎn)組成,屏幕從工廠出來那天物理像素點(diǎn)就固定不變了,也就是我們經(jīng)??吹降氖謾C(jī)分辨率所描述的數(shù)字。
一個像素并不一定是小正方形區(qū)塊,也沒有標(biāo)準(zhǔn)的寬高,只是用于豐富色彩的一個“點(diǎn)”而已。
2.5 屏幕分辨率
屏幕分辨率是指一個屏幕由多少像素組成,常說的分辨率指的就是物理像素。手機(jī)屏幕的橫向和縱向像素點(diǎn)數(shù)以 px 為單位。
iPhone XS Max 和 iPhone SE 的屏幕分辨率分別為 2688x1242 和 1136x640。分辨率越高,屏幕上顯示的像素就越多,單個像素的尺寸也就越小,因此顯示效果更加精細(xì)。
2.6 圖片分辨率
在我們所說的圖像分辨率中,指的是圖像中包含的像素數(shù)量。例如,一張圖像的分辨率為 800 x 400,這意味著圖像在垂直和水平方向上的像素點(diǎn)數(shù)分別為 800 和 400。圖像分辨率越高,圖像越清晰,但它也會受到顯示屏尺寸和分辨率的影響。
如果將 800 x 400 的圖像放大到 1600 x 800 的尺寸,它會比原始圖像模糊。通過圖像分辨率和顯示尺寸,可以計算出 dpi,這是圖像顯示質(zhì)量的指標(biāo)。但它還會受到顯示屏影響,例如最高顯示屏 dpi 為 100,即使圖像 dpi 為 200,最高也只能顯示 100 的質(zhì)量。
可以通過 dpi 和顯示尺寸,計算出圖片原來的像素數(shù)。
這張照片的尺寸為 4x4 英寸,分辨率為 300 dpi,即每英寸有 300 個像素。因此它的實(shí)際像素數(shù)量是寬 1200 像素,高 1200 像素。如果有一張同樣尺寸(4x4 英寸)但分辨率為 72 dpi 的照片,那么它的像素數(shù)量就是寬 288 像素,高 288 像素。當(dāng)你放大這兩張照片時,由于像素數(shù)量的差異,可能會導(dǎo)致細(xì)節(jié)的清晰度不同。
怎么計算 dpi 呢?dpi = 像素數(shù)量 / 尺寸
舉個例子說明:
假設(shè)您有一張寬為1200像素,高為800像素的圖片,您想將其打印成4x6英寸的尺寸。為此,您可以使用以下公式計算分辨率:寬度分辨率 = 1200像素/4英寸 = 300 dpi;高度分辨率 = 800像素/6英寸 = 133.33 dpi。因此,這張圖片的分辨率為300 dpi(寬度)和133.33 dpi(高度)。需要注意的是,計算得出的分辨率僅為參考值,實(shí)際的顯示效果還會受到顯示設(shè)備的限制。
同一尺寸的圖片,同一個設(shè)備下,圖片分辨率越高,圖片越清晰。
2.7 設(shè)備獨(dú)立像素
前面我們說到顯示尺寸,可以使用 CSS 像素來描述圖片在顯示屏上的大小,而 CSS 像素就是設(shè)備獨(dú)立像素。設(shè)備獨(dú)立像素(邏輯像素)dip:device-independent pixels,獨(dú)立于設(shè)備的像素。也叫密度無關(guān)像素。
為什么會有設(shè)備獨(dú)立像素呢?
智能手機(jī)的發(fā)展非常迅速。幾年前,我們使用的手機(jī)分辨率非常低,例如左側(cè)的白色手機(jī),它的分辨率只有320x480。但是,隨著科技的進(jìn)步,低分辨率手機(jī)已經(jīng)無法滿足我們的需求了?,F(xiàn)在,我們有更高分辨率的屏幕,例如右側(cè)的黑色手機(jī),它的分辨率是640x960,是白色手機(jī)的兩倍。因此,如果在這兩個手機(jī)上展示同一張照片,黑色手機(jī)上的每個像素點(diǎn)都對應(yīng)白色手機(jī)上的兩個像素點(diǎn)。
理論上,一個圖片像素對應(yīng)1個設(shè)備物理像素,圖片才能得到完美清晰的展示。因?yàn)楹谏謾C(jī)的分辨率更高,每英寸顯示的像素數(shù)量增多,縮放因素較大,所以圖片被縮小以適應(yīng)更高的像素密度。而在白色手機(jī)的分辨率較低,每英寸顯示的像素數(shù)量較少,縮放因素較小,所以圖片看起來相對較大。
為了解決分辨率越高的手機(jī),頁面元素越來越小的問題,確保在白色手機(jī)和黑色手機(jī)看起來大小一致,就出現(xiàn)了設(shè)備獨(dú)立像素。它可以認(rèn)為是計算機(jī)坐標(biāo)系統(tǒng)中得到一個點(diǎn),這個點(diǎn)代表可以由程序使用的虛擬像素。
例如,一個列表的寬度 300 個獨(dú)立像素,那么在白色手機(jī)會用 300個物理像素去渲染它,而黑色手機(jī)使用 600個物理像素去渲染它,它們大小是一致的,只是清晰度不同。
那么操作系統(tǒng)是怎么知道 300 個獨(dú)立像素,應(yīng)該用多少個物理像素去渲染它呢?這就涉及到設(shè)備像素比。
2.8 設(shè)備像素比
設(shè)備像素比是指物理像素和設(shè)備獨(dú)立像素之間的比例關(guān)系,可以用devicePixelRatio來表示。具體而言,它可以按以下公式計算得出。
設(shè)備像素比:物理像素 / 設(shè)備獨(dú)立像素 // 在某一方向上,x 方向或者 y 方向
在JavaScript中,可以使用window.devicePixelRatio獲取設(shè)備的DPR。設(shè)備像素比有兩個主要目的:
1.保持視覺一致性,以確保相同大小的元素在不同分辨率的屏幕上具有一致的視覺大小,避免在不同設(shè)備上顯示過大或過小的問題。
2.支持高分辨率屏幕,以提供更清晰、更真實(shí)的圖像和文字細(xì)節(jié)。
開發(fā)人員可以使用邏輯像素來布局和設(shè)計網(wǎng)頁或應(yīng)用程序,而不必考慮設(shè)備的物理像素。系統(tǒng)會根據(jù)設(shè)備像素比自動進(jìn)行縮放和適配,以確保內(nèi)容的一致性和最佳顯示效果。
3. 分析原理
3.1 html2canvas 整體流程
在使用html2canvas時,有兩種可選的模式:一種是使用foreignObject,另一種是使用純canvas繪制。
使用第一種模式時,需要經(jīng)過以下步驟:首先將需要截圖的DOM元素進(jìn)行克隆,并在過程中附上getComputedStyle的style屬性,然后將其放入SVG的foreignObject中,最后將SVG序列化成img的src(SVG直接內(nèi)聯(lián))。
1
img.src = "data:image/svg+xml;charset=utf-8," + encodeURIComponent(new XMLSerializer().serializeToString(svg)); 4.ctx.drawImage(img, ....)
第二種模式是使用純Canvas進(jìn)行截圖的步驟。具體步驟如下:
復(fù)制要截圖的DOM,并將其附加樣式。
將復(fù)制的DOM轉(zhuǎn)換為類似于VirtualDOM的對象。
遞歸該對象,根據(jù)其父子關(guān)系和層疊關(guān)系計算出一個renderQueue。
每個renderQueue項(xiàng)目都是一個虛擬DOM對象,根據(jù)之前獲取的樣式信息,調(diào)用ctx的各種方法。
3.2 分析畫布屬性 width、height、scale
通常情況下,每個位圖像素應(yīng)該對應(yīng)一個物理像素,才能呈現(xiàn)完美清晰的圖片。但是在 retina 屏幕下,由于位圖像素點(diǎn)不足,圖片就會變得模糊。
為了確保在不同分辨率的屏幕下輸出的圖片清晰度與屏幕上顯示一致,該程序會取視圖的 dpr 作為默認(rèn)的 scale 值,以及取 dom 的寬高作為畫布的默認(rèn)寬高。這樣,在 dpr 為 2 的屏幕上,對于 800 * 600 的容器畫布,通過 scale * 2 后得到 1600 * 1200 這樣的大圖。通過縮放比打印出來,它的清晰度是跟顯示屏幕一致的。
假設(shè)在 dpr 為 1 的屏幕,假如這里 scale 傳入值為 2,那么寬、高和畫布上下文都乘以 2倍。
為什么要這樣做呢?因?yàn)樵?canvas 中,默認(rèn)情況下,一個單位恰好是一個像素,而縮放變換會改變這種默認(rèn)行為。比如,縮放因子為 0.5 時,單位大小就變成了 0.5 像素,因此形狀會以正常尺寸的一半進(jìn)行繪制;而縮放因子為 2.0 時,單位大小會增加,使一個單位變成兩個像素,形狀會以正常尺寸的兩倍進(jìn)行繪制。
如下例子,通過放大倍數(shù)繪制,輸出一張含有更多像素的大圖
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
// 創(chuàng)建 Canvas 元素
const canvas = document.createElement('canvas');
canvas.width = 200;
canvas.height = 200;
// 獲取繪圖上下文
const ctx = canvas.getContext('2d');
// 繪制矩形
ctx.fillStyle = 'red';
ctx.fillRect(50, 50, 100, 100);
document.body.appendChild(canvas)
//== 放大2倍畫布 ==//
const canvas2 = document.createElement('canvas'); //
// 改變 Canvas 的 width 和 height
canvas2.width = 400;
canvas2.height = 400;
const ctx2 = canvas2.getContext('2d');
// 繪制矩形
ctx2.scale(2, 2);
// 將坐標(biāo)系放大2倍,必須放置在繪制矩形前才生效
ctx2.fillStyle = 'blue';
ctx2.fillRect(50, 50, 100, 100);
document.body.appendChild(canvas2)
3.3 為什么 使用 dpr * 倍數(shù)進(jìn)行 scale
在使用html2Canvas時,默認(rèn)會根據(jù)設(shè)備像素比例(dpr)來輸出與屏幕上顯示的圖片清晰度相同的圖像。但是,如果需要打印更高分辨率的圖像,則需要將dpr乘以相應(yīng)的倍數(shù)。例如,如果我們想要將一張800像素寬,600像素高,72dpi分辨率的屏幕圖片打印在一張8x6英寸,300dpi分辨率的紙上,我們需要確保圖片像素與打印所需像素相同,以保證清晰度。
步驟 1: 將紙的尺寸轉(zhuǎn)換為像素
可以使用打印分辨率來確定轉(zhuǎn)換后的像素尺寸。
假設(shè)打印分辨率為 300 dpi,紙的尺寸為 8x6 英寸,那么:
紙的寬度像素 = 8 英寸 * 300 dpi = 2400 像素
紙的高度像素 = 6 英寸 * 300 dpi = 1800 像素
步驟 2: 計算圖片在紙上的實(shí)際尺寸
將圖片的尺寸與紙的尺寸進(jìn)行比例縮放,以確定在紙上的實(shí)際打印尺寸 。
圖片在紙上的寬度 = (圖片寬度 / 屏幕像素每英寸) * 打印分辨率
圖片在紙上的高度 = (圖片高度 / 屏幕像素每英寸) * 打印分辨率
圖片在紙上的寬度 = (800 / 72) * 300 = 3333.33 像素(約為 3334 像素)
圖片在紙上的高度 = (600 / 72) * 300 = 2500 像素
步驟 3: 調(diào)整圖片大小和打印分辨率
根據(jù)計算出的實(shí)際尺寸,可以將圖片的大小調(diào)整為適合打印的尺寸,并設(shè)置適當(dāng)?shù)拇蛴》直媛省?div style="height:15px;">
也就是說,在保持分辨率為 72 dpi 的情況下,需要把原來 800*600 的圖片,調(diào)整像素為 3334 * 2500。如果是位圖直接放大,就會變糊。如果是矢量圖,就不會有問題。這也是 html2Canvas 最終通過放大 scale 來提高打印清晰度的原因。
在本案例中,我們需要打印出一個可以正常查看的 pdf,對于 A4尺寸,我們可以用 pt 作為單位,其尺寸為 595pt * 841pt。 實(shí)際尺寸為 595/72 = 8.26英寸,841/72 = 11.68英寸。為了打印高清圖片,需要確保每英寸有300個像素,也就是8.26 * 300 = 2478像素,11.68 * 300 = 3504 像素,也就是說 canvas 轉(zhuǎn)出的圖片必須要這么大,最終打印的像素才這么清晰。
而在繪制 DOM 中,由于調(diào)試時不需要這么大,我們可以縮放比例,比如縮小至3倍,這樣圖片大小就為826像素 * 1168像素。如果高度超過1168像素,則需要考慮分頁打印。
// Unit table from <https://github.com/MrRio/jsPDF/blob/ddbfc0f0250ca908f8061a72fa057116b7613e78/jspdf.js#L791>
4. 擴(kuò)展
在理論上,一個位圖像素應(yīng)該對應(yīng)一個物理像素,這樣圖片才能完美清晰地展示。在普通屏幕上,這沒有問題,但在Retina屏幕上,由于位圖像素點(diǎn)不足,圖片會變得模糊。
很明顯,在普通屏幕下(dpr1),200X300(css pixel)img 標(biāo)簽,所對應(yīng)的物理像素個數(shù)就是 200x300 個。而兩倍圖的位圖像素。則是200x300*4,所以就出現(xiàn)一個物理像素點(diǎn)對應(yīng)4個位圖像素點(diǎn),所以它的取色也只能通過一定的算法(顯示結(jié)果就是一張只有原像素總數(shù)四分之一)
5. 總結(jié)
本文介紹了如何通過使用 html2Canvas 來打印高清圖片,并解釋了一些與圖片有關(guān)的術(shù)語,包括英寸、像素、PPI 與 DPI、設(shè)備像素、分辨率等,同時逐步分析了 html2Canvas 打印高清圖片的原理。
到此這篇關(guān)于使用html2Canvas打印高清PDF的原理解析的文章就介紹到這了,更多相關(guān)html2Canvas打印PDF內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!