一、實現HTML頁面保存為圖片
1.1 已知可行方案
現有已知能夠實現網頁保存為圖片的方案包括:
- 方案1:將DOM改寫為canvas,然后利用canvas的toDataURL方法實現將DOM輸出為包含圖片展示的
data URI
- 方案2:使用
html2canvas.js
實現(可選搭配Canvas2Image.js
實現網頁保存為圖片) - 方案3:使用
rasterizeHTML.js
實現
1.2 解決方案的選擇
-
方案1:需要手動計算每個DOM元素的
Computed Style
,然后需要計算好元素在canvas的大小位置等屬性。方案1難點
:- 相當於完全重寫了整個頁面的布局樣式,增加了工作量。
- 由於canvas中沒有的對象概念,對於元素豐富、布局復雜的頁面,不易重構。
- 所有DOM元素改寫進canvas會帶來一些困難,例如:難以支持響應式,圖片元素清晰度不佳和文字點擊區域識別問題等。
- 方案2:該類功能中Github上stars最多(至今仍在維護),Stack Overflow亦有豐富的討論。只需簡單調用html2canvas方法並設定配置項即可。
- 方案3:該方案的限制較多,目前僅支持3類可轉為canvas的目標格式: 頁面url,html字符串和document對象。
小結
: html2canvas是目前實現網頁保存為圖片功能的綜合最佳選擇。
1.3 html2canvas的使用方法
官方GitHub:https://github.com/niklasvh/h...
以下描述針對html2canvas版本是0.5.0-beta4
1.3.1 實現保存為圖片的第一步:html轉為canvas
基於html2canvas.js
可將一個元素渲染為canvas,只需要簡單的調用html2canvas(element[, options]);
即可。下列html2canvas
方法會返回一個包含有<canvas>
元素的promise
:
html2canvas(document.body).then(function(canvas) { document.body.appendChild(canvas); });
1.3.2 實現保存為圖片的第二步:canvas轉image
上一步生成的canvas即為包含目標元素的<canvas>
元素對象。實現保存圖片的目標只需要將canvas轉image即可。
這里的轉換方案有2種
:
- 方案1:基於原生canvas的
toDataURL
方法將canvas輸出為data: URI
類型的圖片地址,再將該圖片地址賦值給<image>
元素的src屬性即可 - 方案2:使用第三方庫
Canvas2Image.js
,調用其convertToImage
方法即可(GitHub)
實際上,Canvas2Image.js
也是基於canvas.toDataURL
的封裝,相比原生的canvas API對於轉為圖片的功能上考慮更為具體(未壓縮的包大小為7.4KB),適合項目使用。
二、生成圖片的清晰度優化方案
2.1 基礎的清晰度優化方案
最終圖片的清晰度
取決於
第一步中html轉換成的canvas的清晰度。
現有解決方案參考;
其基本原理為:
將canvas
的屬性width
和height
屬性放大為2倍(或者設置為devicePixelRatio
倍),最后將canvas的CSS樣式width和height設置為原先1倍的大小。
例如:希望在html中實際顯示的<canvas>
寬高分別為160px
,90px
則可作如下設置
<canvas width="320" height="180" style="width:160px;height:90px;"></canvas>
參考上述文檔具體的使用案例如下:
convert2canvas() { var shareContent = YourTargetElem; var width = shareContent.offsetWidth; var height = shareContent.offsetHeight; var canvas = document.createElement("canvas"); var scale = 2; canvas.width = width * scale; canvas.height = height * scale; canvas.getContext("2d").scale(scale, scale); var opts = { scale: scale, canvas: canvas, logging: true, width: width, height: height }; html2canvas(shareContent, opts).then(function (canvas) { var context = canvas.getContext('2d'); var img = Canvas2Image.convertToImage(canvas, canvas.width, canvas.height); document.body.appendChild(img); $(img).css({ "width": canvas.width / 2 + "px", "height": canvas.height / 2 + "px", }) }); }
2.2 進階的清晰度優化方案
上述設置可以解決通常情況下圖片不清晰的問題,不過探索並沒有結束。
實際在我們的項目中,即使作出2.1節
的設置后,大果粒一般的渲染結果依然尷尬。
下面直接給出3條進一步的優化策略:
- 更改
百分比布局
為px布局
(如果原先是百分比布局的話) 關閉
canvas默認的抗鋸齒設
置- 設置模糊元素的
width
和height
為素材原有寬高,然后通過transform: scale
進行縮放。這里scale
的數值由具體需求決定。
基本原理
- 如果原來使用百分比設置元素寬高,請更改為
px為單位
的寬高,避免樣式二次計算導致的模糊 - 默認情況下,canvas的抗鋸齒是開啟的,需要
關閉抗鋸齒
來實現圖像的銳化(MDN: imageSmoothingEnabled ) - 除了canvas可以通過擴大2倍寬高然后縮放至原有寬高來提高清晰度,對於DOM中其他的元素也可以使用
css樣式
的scale
來實現同樣的縮放
例: html2canvas配置
convert2canvas() { var cntElem = $('#j-sec-end')[0]; var shareContent = cntElem; //需要截圖的包裹的(原生的)DOM 對象 var width = shareContent.offsetWidth; //獲取dom 寬度 var height = shareContent.offsetHeight; //獲取dom 高度 var canvas = document.createElement("canvas"); //創建一個canvas節點 var scale = 2; //定義任意放大倍數 支持小數 canvas.width = width * scale; //定義canvas 寬度 * 縮放 canvas.height = height * scale; //定義canvas高度 *縮放 canvas.getContext("2d").scale(scale, scale); //獲取context,設置scale var opts = { scale: scale, // 添加的scale 參數 canvas: canvas, //自定義 canvas // logging: true, //日志開關,便於查看html2canvas的內部執行流程 width: width, //dom 原始寬度 height: height, useCORS: true // 【重要】開啟跨域配置 }; html2canvas(shareContent, opts).then(function(canvas) { var context = canvas.getContext('2d'); // 【重要】關閉抗鋸齒 context.mozImageSmoothingEnabled = false; context.webkitImageSmoothingEnabled = false; context.msImageSmoothingEnabled = false; context.imageSmoothingEnabled = false; // 【重要】默認轉化的格式為png,也可設置為其他格式 var img = Canvas2Image.convertToJPEG(canvas, canvas.width, canvas.height); document.body.appendChild(img); $(img).css({ "width": canvas.width / 2 + "px", "height": canvas.height / 2 + "px", }).addClass('f-full'); }); }
例: DOM元素樣式:
.targetElem {width: 54px;height: 142px;margin-top:2px;margin-left:17px;transform: scale(0.5)}
三、含有跨域圖片的配置
由於canvas對於圖片資源的同源限制
,如果畫布中包含跨域的圖片資源則會污染畫布,造成生成圖片樣式混亂或者html2canvas方法不執行等問題。
以下主要解決兩類跨域的圖片資源:包括已配置過CORS的CDN
中的圖片資源和微信用戶頭像
圖片資源。
3.1 針對CDN中的圖片的配置
- 要求CDN的圖片配置好
CORS
。CDN
配置好后,通過chrome開發者工具可以看到響應頭中應含有Access-Control-Allow-Origin
的字段。 - 開啟
html2canvas
的useCORS
配置項。即作如下設置:
var opts = {useCORS: true};
html2canvas(element, opts);
注意
:
如果沒有開啟html2canvas
的useCORS
配置項,html2canvas
會正常執行且不會報錯,但是不會輸出對應的CDN圖片
(已測試同時包含CDN的圖片
和本地圖片
的資源的頁面,但是只有本地圖片
能夠被正常渲染出來)
3.2 針對微信用戶頭像的配置
如果需要將微信
平台中的用戶頭像一並保存為圖片,3.1
的方案無能為力。可通過配置服務端代理轉發
(forward)實現,此處不贅述。
其他注意事項
1. margin的遮擋問題
微信中,喚出長按保存圖片
的菜單要求長按的對象直接是<image>
元素,如果<image>
元素上方存在遮擋,則不會喚出菜單。
而事實上,引發遮擋的並不只是非<image>
元素,還可能是margin
屬性。例如:若在頁面底部,對一個絕對定位的元素設置了數值很大的margin-top
,則margin-top
所涉及的區域,均無法長按喚出菜單。解決方案:將margin-top
改用為top
即可。
2. 安卓版微信保存圖片失敗的問題
canvas2img
默認保存圖片的格式為png
,而在安卓版微信中所生成的圖片盡管能長按喚出保存圖片的菜單,但是無法正確保存到本地相冊。 解決方案
:設置canvas2img
的生成圖片格式配置項為jpeg
即可。
3. JPEG的黑屏問題
設置canvas2img
輸出格式為jpeg
,會有一定幾率導致生成的圖片包含大量的黑色塊。可能的解決方案
:縮減部分圖片元素的體積和尺寸大小。
4. 不能保留動效
在圖片的轉化前
,必須停止
或者刪除動效后才能正確渲染出圖片,否則生成的圖片是破裂的。