亚洲乱码中文字幕综合,中国熟女仑乱hd,亚洲精品乱拍国产一区二区三区,一本大道卡一卡二卡三乱码全集资源,又粗又黄又硬又爽的免费视频

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

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

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

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

截圖對(duì)比

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

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

  let time2 = Date.now()
  console.log(`html2canvas 生成圖片耗時(shí):${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 生成圖片耗時(shí):${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 生成圖片耗時(shí):${time2 - time1}ms`)
    const dataUrl = canvas.toDataURL("image/png");
    var link = document.createElement('a');
    link.download = 'captureForHtml2img.png';
    link.href = dataUrl;
    link.click();
  });
}

首先測(cè)試了長(zhǎng)頁面的結(jié)果如下:

html2canvas 生成圖片耗時(shí):377ms
snapdom 生成圖片耗時(shí):1771ms
html2img 生成圖片耗時(shí):297ms

然后我把長(zhǎng)頁面改成高度只有1168px的再測(cè)一次

html2canvas 生成圖片耗時(shí):273ms
snapdom 生成圖片耗時(shí):547ms
html2img 生成圖片耗時(shí):60ms

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

截圖原理

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

html2canvas

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

  • 利用svg里面的foreignObject標(biāo)簽
  • 手動(dòng)一個(gè)個(gè)節(jié)點(diǎn)畫上去

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

foreignObject

這種方式大概的流程首先把克隆下來的目標(biāo)節(jié)點(diǎn)插入到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對(duì)象加載成img給接下來的canvas通過drawImage畫出來。得到一個(gè)目標(biāo)畫了目標(biāo)節(jié)點(diǎn)的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);

手動(dòng)畫

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

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

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

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

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

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

    // 6. 返回整個(gè) root stacking context
    return root;
};

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

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

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

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

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)}`;

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

capture
toRaw
toImg
toCanvas
toBlob
toPng
toJpg
toWebp
download

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

html-to-image

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

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)
}

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

總結(jié)

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

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

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

相關(guān)文章

最新評(píng)論