把HTML插入canvas實現網頁截圖

尛沫發表於2014-05-30

將DOM內容HTML繪製到畫布中是有可能的,但如何有把握並且安全地實現它,就應該按照規範行事。你不能把HTML畫到canvas上。相反,你需要使用一個SVG影像,其中包含你想要呈現的內容。可以使用<foreignobject>元素包含HTML內容,之後把這個svg繪製到你的canvas中。

唯一真正棘手的事情可能是建立SVG影像,所有你需要做的是建立一個包含XML字串的SVG,然後按照下面的步驟構造一個Blob:

blob物件的媒體型別mime為 “image/svg+xml”  
<svg> 元素.  
在svg元素中包含 <foreignobject> 元素.
(格式化好的)HTML,被包裹到<foreignobject>中.

如上所述通過使用一個object URL,我們可以內聯HTML而不是從外部源載入它。當然,如果你喜歡,可以使用外部源,只要域與原始檔案相同,比如:

<!DOCTYPE html>
<html>
<body>
<p><canvas id="canvas" style="border:2px solid black;" width="200" height="200"></canvas>
<script>
var canvas = document.getElementById("canvas");
var ctx = canvas.getContext("2d");
var data = "<svg xmlns='http://www.w3.org/2000/svg' width='200' height='200'>" +
             "<foreignObject width='100%' height='100%'>" +
               "<div xmlns='http://www.w3.org/1999/xhtml' style='font-size:40px'>" +
                 "<em>I</em> like <span style='color:white; text-shadow:0 0 2px blue;'>cheese</span>" +
               "</div>" +
             "</foreignObject>" +
           "</svg>";
var DOMURL = self.URL || self.webkitURL || self;
var img = new Image();
var svg = new Blob([data], {type: "image/svg+xml;charset=utf-8"});
var url = DOMURL.createObjectURL(svg);
img.onload = function() {
    ctx.drawImage(img, 0, 0);
    DOMURL.revokeObjectURL(url);
};
img.src = url;
</script>
</body>
</html>

data變數設定了SVG影像的內容(這包括HTML),我們希望繪製到我們的canvas中。通過呼叫 new Image()我們建立一個新的html < img>元素,新增資料進去,指定一個object URL,之後在圖片onload的時候呼叫 drawImage() 來把圖片繪製到畫布中。

您可能想知道這種方式是否安全,擔心canvas會讀取敏感資料。答案是這樣的:這個解決方案的實現依賴的SVG影像是非常嚴格的。SVG影像不允許載入任何外部資源,即使似乎來自同一個域。資源如柵格影像(如JPEG影像)或< iframe>s 需要用 data: URIs來內聯引入。

此外,你不能在一個SVG影像中引入指令碼檔案,所以沒有從其他指令碼訪問DOM的風險,而且DOM元素在SVG影像中不能接收事件的輸入,所以沒有辦法通過把隱私資訊載入到一個表單控制元件(如一個檔案的完整路徑< input> 元素)然後渲染出來,之後通過讀取畫素把這些資訊取出。

訪問過的連結風格並不應用於SVG影像中呈現的連結,所以歷史資訊也不能被檢索,本地的主題也不呈現在SVG影像中,這使得它很難確定使用者的平臺。

生成的canvas元素是純淨的,意味著你可以通過呼叫 toBlob(function(blob){…})來返回canvas的blob,或者toDataURL()來返回Base64-編碼的data: URI。

SVG必須是合法的XML,你需要解析並把HTML轉為規範的符合格式的。下面的程式碼可以很方便地解析HTML:

var doc = document.implementation.createHTMLDocument("");
doc.write(html);

// You must manually set the xmlns if you intend to immediately serialize the HTML
// document to a string as opposed to appending it to a <foreignObject> in the DOM
doc.documentElement.setAttribute("xmlns", doc.documentElement.namespaceURI);

// Get well-formed markup
html = (new XMLSerializer).serializeToString(doc);

本文為Anyforweb技術分享部落格,需要了解網站建設及更多Web應用相關資訊,請訪問anyforweb.com。

相關文章