前言
網頁截圖匯出不是一個非常高頻的需求,但時不時的也會遇到。這裡總結一下系統的解決方案,然後從中選擇合適自己的。
截圖匯出可以看到是兩個功能,第一步實現截圖,第二步實現匯出也就是下載能力。
截圖實現
首先,我們必須明白正常javascript是執行在瀏覽器裡的,本身沒有截圖的能力。所以要想實現截圖,必須通過其他迂迴方案實現,廢話少說,直接上結論。
前端方案1 canvas
實現原理:
html2canvas 是一個 HTML 渲染器,螢幕截圖是基於 DOM,因此生成的圖片並不一定 100% 一致,因為它沒有製作實際的螢幕截圖,而是根據頁面上可用的資訊構建螢幕截圖。
文件介紹的比較清楚,canvas只是去還原dom的展示效果。
根據實現原理,可以想象,實現成本還是比較高的,需要解析dom和css樣式,而且css樣式不一定能完美對映到canvas,另外還會受限於canvas收到的一些限制,比如跨域資源問題。
前端方案2 svg
實現原理:
核心要素是SVG 的一個特性,允許在 <foreignObject> 標籤中包含任意的 HTML 內容。所以為了渲染該dom節點,需要如下步驟:
- 遞迴克隆原始DOM節點
- 計算節點和每個子節點的樣式並將其複製到相應的克隆,並且要重新建立偽元素,因為它們當然不會以任何方式克隆
- 嵌入網頁字型,連結所有css樣式到style標籤,應用到clone節點
- 嵌入影像
在 <img> 元素中嵌入影像 URL
背景 CSS 屬性中使用的內嵌影像,以類似於字型的方式 - 將克隆的節點序列化為 XML
- 將 XML 包裹到 <foreignObject> 標籤中,然後包裹到 SVG 中,然後使其成為資料 URL
- 建立一個以 SVG 作為源的 Image 元素,並將其呈現在您也已建立的離屏畫布上,然後從畫布中讀取內容
嗯,這就是svg方式實現了,和canvas方式一樣,需要我們處理dom,css和資源,但是後續的繪製渲染工作交給了瀏覽器,所以減輕了很多工作量和程式碼量。
服務端方案
代表方式 puppeteer實現
Puppeteer 是一個 Node 庫,它提供了一個高階 API 來通過 DevTools 協議控制 Chromium 或 Chrome。這裡具體就不介紹了,我們只使用它的截圖功能。
這個就是真正的截圖能力了,官網有demo。
考慮到業務層的頁面都需要使用者訪問許可權,所以正確的步驟應該是:
- 啟動一個node服務,利用puppeter實現截圖能力
- 客戶端發起一個請求,攜帶cookie
- 第一步啟動的服務處理此請求,生成圖片,地址返回給客戶端
- 客戶端拿到圖片資源,進行下載即可
對比
html2canvas
優點
簡單頁面截圖效果還可以,相容性還好
缺點
- 部分樣式無法支援
- 複雜頁面,慘不忍睹。
- cors可以解決跨域,但是實際應用還是問題百出
- 實現非常複雜,難以改動
在我們專案幾乎無法使用
dom-to-image
優點
- 實現簡單,可下載後自行改動原始碼
- 還原度良好,尤其chrome支援非常好
缺點
- safari支援不是很好,不過一些問題可以解決
基本能支援業務需求
後端截圖
優點
- 真實截圖,終極方案,無相容問題
缺點
- 實現流程稍微繁瑣
- 需要考慮 使用者許可權認證問題
匯出實現
匯出也就是下載檔案到本地
a標籤
原理是通過a標籤的download屬性實現下載。
// 基本使用
<a href="/images/xxxxx.jpg" download="filename">
// 指令碼觸發
const download = (filename, url) => {
let a = document.createElement('a');
a.style = 'display: none'; // 建立一個隱藏的a標籤
a.download = filename;
a.href = url;
document.body.appendChild(a);
a.click(); // 觸發a標籤的click事件
document.body.removeChild(a);
}
blob檔案流物件
這個需要服務端支援,發起請求,服務端返回資料流,然後前端觸發下載。
具體就不多說了,網上有很多文章。
這裡推薦幾個封裝好的下載檔案庫
downloadjs
file-saver