瀏覽器列印的兩種實踐方案

語言: CN / TW / HK

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 內,並呼叫 iframeprint 方法

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

  • 簡單說就是將網頁通過 html2canvasdom-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 裡面的內容,於是我遍歷整個 iframedom 替換,此外我們還要書寫邏輯手動對圖片指定寬高進行截圖分頁,此部分可以將其封裝下方便複用,具體方案如下

```html

```

注意列印完之後最好在頂層元件封裝個 reload 方法關聯 v-if 通過 provide 提供下去,元件呼叫下重新渲染一遍(因為之前我們替換了頁面元素,不重新整理還原可能會有問題)

js reload() { this.isRouteAlive = false; this.$nextTick(() => { this.isRouteAlive = true; }); },

最後這個截斷問題比較棘手,我們可以這樣解決

  • 設定對應轉換後的 canvas 為白色背景
  • 轉換圖片後獲取對應截斷處的圖片畫素
  • 從截斷處一行行往上掃描畫素點顏色
  • 碰到這一行是全白的,代表從這裡截斷,將這個高度往下的內容放到下一頁

具體程式碼如下!

```html

```