欧美bbbwbbbw肥妇,免费乱码人妻系列日韩,一级黄片

JS實現(xiàn)網(wǎng)頁截圖的三種方式對比

 更新時間:2025年08月28日 09:45:00   作者:K仔  
這篇文章主要為大家詳細介紹了JS實現(xiàn)網(wǎng)頁截圖的三種方式并進行對比,即html2canvas,snapdom和html-to-image,下面小編就來和大家詳細介紹一下吧

網(wǎng)頁截圖的三種方法

我讓Ai幫我生成了一個官網(wǎng)首頁的長頁面,高度大概是7380px.接下來測試一下同樣生成png的時間。

截圖對比

實現(xiàn)截圖的代碼如下。主要通過埋入當(dāng)前時間與最后生成圖片的時間來做對比。

const captureForHtml2canvas = async () => {
  let time1 = Date.now()
  const el = document.getElementById('captureBody');
  // 把 DOM 節(jié)點轉(zhuǎn)成 canvas
  const canvas = await html2canvas(el, {
    backgroundColor: null, 
    useCORS: true,         // 允許跨域圖片(如果有)
  });

  let time2 = Date.now()
  console.log(`html2canvas 生成圖片耗時:${time2 - time1}ms`)
  // 轉(zhuǎn)成 base64 PNG
  const dataUrl = canvas.toDataURL("image/png");

  // 生成下載鏈接并觸發(fā)
  const link = document.createElement("a");
  link.href = dataUrl;
  link.download = "captureForHtml2canvas.png";
  link.click();
}
const captureForSnapdom = async () => {
  let time1 = Date.now()
  const el = document.getElementById('captureBody');
  // const result = await snapdom(el, { scale: 1 });
  const canvas = await snapdom.toCanvas(el)
  let time2 = Date.now()
  console.log(`snapdom 生成圖片耗時:${time2 - time1}ms`)
  // 轉(zhuǎn)成 base64 PNG
  const dataUrl = canvas.toDataURL("image/png");
  // 生成下載鏈接并觸發(fā)
  var link = document.createElement('a');
  link.download = 'captureForSnapdom.png';
  link.href = dataUrl;
  link.click();
}
const captureForHtml2img = () => {
  let time1 = Date.now()
  htmlToImage
  .toCanvas(document.getElementById('captureBody'))
  .then((canvas) => {
    let time2 = Date.now()
    console.log(`html2img 生成圖片耗時:${time2 - time1}ms`)
    const dataUrl = canvas.toDataURL("image/png");
    var link = document.createElement('a');
    link.download = 'captureForHtml2img.png';
    link.href = dataUrl;
    link.click();
  });
}

首先測試了長頁面的結(jié)果如下:

html2canvas 生成圖片耗時:377ms
snapdom 生成圖片耗時:1771ms
html2img 生成圖片耗時:297ms

然后我把長頁面改成高度只有1168px的再測一次

html2canvas 生成圖片耗時:273ms
snapdom 生成圖片耗時:547ms
html2img 生成圖片耗時:60ms

這個測試結(jié)果有點驚訝,按snapdom的說法主要利用瀏覽器自帶的Api來實現(xiàn)理論上應(yīng)該是最快的。

截圖原理

上面的測試結(jié)果有待研究一下,不過我們可以看看他們各自的實現(xiàn)邏輯大概是怎樣的。

html2canvas

看了一下源碼發(fā)現(xiàn)html2canvas支持兩種模式。

  • 利用svg里面的foreignObject標(biāo)簽
  • 手動一個個節(jié)點畫上去

以上兩種方式在真正截圖之前都會先克隆一份目標(biāo)節(jié)點,主要都是利用Element.cloneNode方法。

foreignObject

這種方式大概的流程首先把克隆下來的目標(biāo)節(jié)點插入到svg的foreignObject標(biāo)簽中。

const xmlns = 'http://www.w3.org/2000/svg';
const svg = document.createElementNS(xmlns, 'svg');
const foreignObject = document.createElementNS(xmlns, 'foreignObject');
svg.appendChild(foreignObject);
foreignObject.appendChild(cloneNode);

然后把得到的svg對象加載成img給接下來的canvas通過drawImage畫出來。得到一個目標(biāo)畫了目標(biāo)節(jié)點的canvas。

// 加載svg
const img = new Image();
img.onload = () => {
    resolve(img);
};
img.onerror = reject;

img.src = `data:image/svg+xml;charset=utf-8,${encodeURIComponent(new XMLSerializer().serializeToString(svg))}`;

// 畫出來
ctx.drawImage(img, -this.options.x * this.options.scale, -this.options.y * this.options.scale);

手動畫

這種方式主要是把克隆的目標(biāo)節(jié)點遍歷畫出來,它會先把所有節(jié)點包裝成類似可繪制的對象指令,然后利用stacking context建立一個新的“繪制層”,最后按照順序畫到canvas上。主要遍歷邏輯在以下代碼。

export const parseStackingContexts = (container: ElementContainer): StackingContext => {
    // 1. 把 DOM 容器包一層 "可繪制對象"
    const paintContainer = new ElementPaint(container, null);

    // 2. 用這個 paintContainer 創(chuàng)建一個根 stacking context
    const root = new StackingContext(paintContainer);

    // 3. 創(chuàng)建一個數(shù)組,用來收集 "list-item" 元素
    const listItems: ElementPaint[] = [];

    // 4. 遞歸解析 DOM 樹,建立 stacking context 樹
    parseStackTree(paintContainer, root, root, listItems);

    // 5. 額外處理 <li>(list-item)相關(guān)的 marker(比如列表圓點/數(shù)字)
    processListItems(paintContainer.container, listItems);

    // 6. 返回整個 root stacking context
    return root;
};

關(guān)鍵觸發(fā)繪制層是在parseStackTree方法中遍歷去構(gòu)建繪制的dom樹。

我嘗試使用foreignObject的模式去截圖結(jié)果對比如下:

//小圖
html2canvas (foreignObject方式)生成圖片耗時:220ms
html2canvas 生成圖片耗時:246ms
// 大圖
html2canvas (foreignObject方式)生成圖片耗時:337ms
html2canvas 生成圖片耗時:529ms

長大圖的時候foreignObject的效率還是比較高的。所以我理解中snapdom應(yīng)該效率速度是最高的才對,也有可能是我使用的姿勢不對。

snapdom

snapdom的核心截圖方式主要和html2canvas的foreignObjectRendering是一樣的。核心如下:

const svgNS = "http://www.w3.org/2000/svg";
const fo = document.createElementNS(svgNS, "foreignObject");
fo.setAttribute("width", "100%");
fo.setAttribute("height", "100%");
const styleTag = document.createElement("style");
styleTag.textContent = baseCSS + fontsCSS + "svg{overflow:visible;}" + classCSS;
fo.appendChild(styleTag);
fo.appendChild(clone);
const serializer = new XMLSerializer();
const foString = serializer.serializeToString(fo);
const svgHeader = `<svg xmlns="${svgNS}" width="${w}" height="${h}" viewBox="0 0 ${w} ${h}">`;
const svgFooter = "</svg>";
svgString = svgHeader + foString + svgFooter;
dataURL = `data:image/svg+xml;charset=utf-8,${encodeURIComponent(svgString)}`;

它會先得到一個dataURL,后續(xù)再根據(jù)不同的需求最終再做轉(zhuǎn)換,例如toImg會生成一個Image節(jié)點,toCanvas會生成canvas節(jié)點。它提供了實例方法如下:

capture
toRaw
toImg
toCanvas
toBlob
toPng
toJpg
toWebp
download

也可以通過snapdom.xx直接調(diào)用。

html-to-image

這個庫的提供的API最終都是調(diào)用到toSvg,其核心原理還是利用svg的foreignObject對象。并且在創(chuàng)建對象前還是會利用cloneNode去克隆目標(biāo)節(jié)點。

export async function nodeToDataURL(
  node: HTMLElement,
  width: number,
  height: number,
): Promise<string> {
  const xmlns = 'http://www.w3.org/2000/svg'
  const svg = document.createElementNS(xmlns, 'svg')
  const foreignObject = document.createElementNS(xmlns, 'foreignObject')

  svg.appendChild(foreignObject)
  foreignObject.appendChild(node)
  return svgToDataURL(svg)
}

然后就會得到一個svg。例如我前面用的toCanvas方法它就是先調(diào)用toSvg得到svg對象然后轉(zhuǎn)換成圖片畫到一個新的canvas對象上并返回。

總結(jié)

三種庫核心都是利用到svg的foreignObject對象插入目標(biāo)節(jié)點,然后再通過不同的方式渲染生成,大同小異,只是html2canvas默認不使用該方式。

另外一個就是官方提供的數(shù)據(jù)在實際業(yè)務(wù)中使用時不一定是最好的,大家在業(yè)務(wù)中可多嘗試對比一下真實效果與數(shù)據(jù)再決定使用哪種。

到此這篇關(guān)于JS實現(xiàn)網(wǎng)頁截圖的三種方式對比的文章就介紹到這了,更多相關(guān)js網(wǎng)頁截圖內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!

相關(guān)文章

最新評論