DOM生成图片原理

本文详细介绍了如何将DOM转换为图片,包括复制DOM并序列化、嵌入SVG foreignObject以及通过canvas生成图片的步骤。同时,文章讨论了在转换过程中遇到的样式丢失和字体丢失问题,并提出了解决方案,如内联样式和嵌入字体资源。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

工作原理

使用svg的一个特性,允许在<foreignobject>标签中包含任意的html内容。(主要是 XMLSerializer | MDN这个apidom转为svg) 所以,为了渲染那个dom节点,你需要采取以下步骤:

  1. 递归 clone 原始的 dom 节点
  2. 获取 节点以及子节点 上的 computed style,并将这些样式添加进新建的style标签中(不要忘记了clone 伪元素的样式)
  3. 嵌入网页字体
  • 找到所有的@font-face
  • 解析URL资源,并下载对应的资源
  • base64编码和内联资源 作为 data: URLS引用
  • 把上面处理完的css rules全部都放进<style>中,并把标签加入到clone的节点中去
  1. 内嵌图片(都转成dataUrl)
  • 内联图片src 的url 进 <img>元素
  • 背景图片 使用 background css 属性,类似fonts的使用方式
  1. 序列化 clone 的 dom 节点 为 svg
  2. 将xml包装到<foreignobject>标签中,放入svg中,然后将其作为data: url
  3. 将png内容或原始数据作为uint8array获取,使用svg作为源创建一个img标签,并将其渲染到新创建的canvas上,然后把canvas转为base64
  4. 完成

1. 复制 DOM 并序列化

const cloneNode = document.body.cloneNode(true);
const xmlSerializer = new XMLSerializer();
const html = xmlSerializer.serializeToString(cloneNode);

2. 嵌入 svg foreignObject

const svg = `
  <svg
    xmlns='http://www.w3.org/2000/svg'
    width='${document.body.clientWidth}'
    height='${document.body.clientHeight}'
  >
    <foreignObject
      x='0'
      y='0'
      width='100%'
      height='100%'
    >
      ${html}
    </foreignObject>
  </svg>
`;

3.通过 canvas 生成图片

const canvas = document.createElement('canvas');
canvas.width = document.body.clientWidth;
canvas.height = document.body.clientHeight;
const ctx = canvas.getContext('2d');
const img = new Image();
img.onload = () => {
  ctx.drawImage(img, 0, 0);
  canvas.toBlob((blob) => {
    const blobURL = URL.createObjectURL(blob);
    window.open(blobURL);
    URL.revokeObjectURL(blobURL);
  });
};
img.src = `data:image/svg+xml;charset=utf-8,${svg}`;

问题:图片内容无样式

svg 以字符串的形式通过 img src data 加载,不与当前页面共享样式。

市面上的截图插件 html2canvasdom-to-image 都是通过内联样式的方式解决此问题。 深度遍历每个源 DOM 元素,每个 DOM 元素通过 window.getComputedStyle 方法当前元素的所有样式属性和值。 找到相应的克隆 DOM 元素,通过 getPropertyValue 方法和 setProperty 方法重新赋值。

问题:图片内容字体丢失

跟图片内容样式丢失原因一样,字体也需要嵌入 svg。 因为不与当前页面共享资源,字体资源引用不能使用 URL 形式,需要转换成 base64 格式。

const response = await fetch(url, { headers: { responseType: 'blob' } });
const blob = await response.blob();
const fileReader = new FileReader();
fileReader.onload = () => {
  const b64 = fileReader.result;
};
fileReader.readAsDataURL(blob);

svgStyle 添加字体声明。

svgStyle += `
	@font-face {
		font-family: "${name}";
		src: local("${name}"), url("${b64}");
	}
`;

参考链接

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值