工作原理
使用svg 的一个特性,允许在<foreignobject> 标签中包含任意的html 内容。(主要是 XMLSerializer | MDN这个api 将dom 转为svg ) 所以,为了渲染那个dom 节点,你需要采取以下步骤:
- 递归
clone 原始的 dom 节点 - 获取 节点以及子节点 上的
computed style ,并将这些样式添加进新建的style标签中(不要忘记了clone 伪元素的样式) - 嵌入网页字体
- 找到所有的
@font-face - 解析URL资源,并下载对应的资源
- base64编码和内联资源 作为
data: URLS引用 - 把上面处理完的
css rules 全部都放进<style> 中,并把标签加入到clone的节点中去
- 内嵌图片(都转成dataUrl)
- 内联图片src 的url 进
<img>元素 - 背景图片 使用 background css 属性,类似fonts的使用方式
- 序列化 clone 的 dom 节点 为
svg - 将xml包装到
<foreignobject> 标签中,放入svg 中,然后将其作为data: url - 将png内容或原始数据作为
uint8array 获取,使用svg作为源创建一个img 标签,并将其渲染到新创建的canvas 上,然后把canvas 转为base64 - 完成
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 加载,不与当前页面共享样式。
市面上的截图插件 html2canvas、dom-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}");
}
`;
参考链接
|