背景
如今很多網站都引入截圖功能,可用于問題反饋、內容分享等實用需求,而前端截圖也不知不覺成為了首選,今天為大家推薦兩種前端截圖方式,雖然有些局限,但是也能應付大部分專案需求,
- Canvas截圖:
html2canvas - SVG截圖:
rasterizehtml
原理
首先來談下兩種前端截圖方式的原理,雖然實作方式不太一致,但是核心思想是相同的,
以html2canvas為代表的Canvas截圖,通過遍歷DOM克隆一份副本,將此副本在Canvas上重新繪制,并根據DOM的樣式應用在對應的繪制元素上,再通過Canvas生成圖片,轉換程序可理解成:DOM→Canvas→Image,
以rasterizehtml為代表的SVG截圖,通過遍歷DOM克隆一份副本,利用SVG的foreignObject把DOM作為外部資源嵌套在SVG中,將此SVG在Canvas上重新繪制,并根據DOM的樣式應用在對應的繪制元素上,再通過Canvas生成圖片,轉換程序可理解成:DOM→SVG的ForeignObject→Canvas→Image,
兩種前端截圖方式最后都是通過把DOM繪制到Canvas,再通過Canvas輸出圖片,
限制
雖然兩種前端截圖方式都有這兩個封裝得比較完善的第三方庫html2canvas和rasterizehtml,但是由于在轉換程序中存在一些自身的局限性,所以也導致截圖可能出現一些不完美的問題,
Canvas截圖的限制性
- 無法渲染
跨域資源(支持同域) - 無法渲染
iFrame和Flash內容(支持SVG)
SVG截圖的限制性
- 無法渲染
跨域資源(支持同域) - 無法渲染如
lazyload等通過JS加載的資源 - 無法渲染
行內background-image或JS操作background-image
方案
不多廢話,直接上兩種前端截圖方式的代碼,小伙伴們可根據專案需求自行優化代碼和增加功能哈,
準備
<div id="screenshot">Hello World</div>
<button id="save-btn">保存</button>
// 渲染圖片
function Render(src, width, height, cb) {
const img = new Image();
img.src = https://www.cnblogs.com/onesea/p/src;
img.width = width;
img.height = height;
img.crossOrigin = ""; // 影像跨域時配置
cb && cb(img);
}
// 下載圖片
function Download(url, name) {
const target = document.createElement("a");
target.href = https://www.cnblogs.com/onesea/p/url;
target.download = name;
const event = document.createEvent("MouseEvents");
event.initEvent("click", true, true);
target.dispatchEvent(event);
}
Canvas截圖
import Html2canvas from "html2canvas";
const btn = document.getElementById("save-btn");
btn.addEventListener("click", () => {
const screenshot = document.getElementById("screenshot");
// allowTaint: true, // 不能與useCORS共用
const opts = {
logging: false,
scale: 2,
useCORS: true
};
Html2canvas(screenshot, opts).then(res => {
const { height, width } = res;
const base64 = res.toDataURL("image/png", 1);
Render(base64, width, height, img => {
document.body.appendChild(img);
Download(base64, "screenshot.png");
});
}, err => alert("截圖失敗,請重新嘗試"));
});
SVG截圖
import Rasterizehtml from "rasterizehtml";
const btn = document.getElementById("save-btn");
btn.addEventListener("click", () => {
// drawURL()加載的URL必須是同域名URL或支持跨域的URL
// 下面的URL是隨便寫的,記得換成同域名URL或支持跨域的URL
const url = "https://www.baidu.com";
const canvas = document.createElement("canvas");
const opts = {
executeJs: true,
height: screen.height,
width: screen.width
};
Rasterizehtml.drawURL(url, canvas, opts).then(res => {
const base64 = "data:image/svg+xml;base64," + btoa(unescape(encodeURIComponent(res.svg)));
Render(base64, opts.width, opts.height, img => {
document.body.appendChild(img);
Download(base64, "screenshot.png");
});
}, err => alert("截圖失敗,請重新嘗試"));
});
另外還有幾點需要注意一下:
- 使用Canvas截圖兼容低版本瀏覽器時,不能使用
CSS3屬性和帶有前綴的屬性 - 使用SVG截圖可獲取同域
<iframe>內容進行渲染 - 截圖不能包含跨域獲取的內容,否則不會渲染跨域內容
總結
淺談兩種前端截圖方式就到此為止啦,相信小伙伴們對前端截圖也有一個比較清晰的概念了,可結合自身專案嘗試一下兩種前端截圖方式,探究下其相同點和不同點,如果對其截圖原理感興趣,可剖析下html2canvas和rasterizehtml的原始碼,相信你會有意外的識訓喔!
轉載請註明出處,本文鏈接:https://www.uj5u.com/qiye/550356.html
標籤:JavaScript
上一篇:javascript
