直接用 blob 生成海报失败是因为 todataurl() 返回 base64 字符串,而 blob 构造函数需 arraybuffer/typedarray,传字符串会导致编码错误、图片头损坏;正确做法是用 canvas.toblob() 或手动 atob() 解码 base64 再构造 blob。

为什么直接用 Blob 生成海报文件会失败
很多人一上来就调 new Blob([canvas.toDataURL()], {type: 'image/png'}),结果下载出来是乱码或空白——因为 toDataURL() 返回的是 base64 字符串,不是二进制数据。Blob 构造函数只接受 ArrayBuffer、TypedArray 或 Blob 本身,传字符串进去,它会按 UTF-8 编码存,导致图片头损坏。
正确路径是:先用 canvas.toBlob()(原生支持)或手动解码 base64(兼容性兜底)拿到原始字节,再构造 Blob。
-
canvas.toBlob()是首选,它直接产出Blob,无需中间转换 - 若需兼容 IE 或旧安卓 WebView,才用
atob()+Uint8Array手动还原 - 注意:某些低版本 Safari 对
toBlob()的 MIME 类型敏感,指定'image/jpeg'时必须确保 canvas 内容不含透明通道,否则可能静默失败
如何把多层 DOM 元素(含文字、SVG、图片)转成一张可下载的海报
核心是用 html2canvas 把目标容器渲染为 canvas,但它默认不处理跨域图片、字体加载延迟、CSS 变量等。常见表现是文字缺失、图片白块、布局错位。
- 务必在调用前预加载所有远程图片:
img.crossOrigin = 'anonymous',并等待img.onload - 避免使用 Web Font(如 Google Fonts),改用系统字体或内联
@font-face的 base64 字体数据 - 对含
transform或filter的元素,加useCORS: true和allowTaint: true(仅限同源) - 示例关键调用:
html2canvas(container, { useCORS: true, allowTaint: true, scale: window.devicePixelRatio || 2, logging: false }).then(canvas => { canvas.toBlob(blob => { const url = URL.createObjectURL(blob); const a = document.createElement('a'); a.href = url; a.download = 'poster.png'; a.click(); URL.revokeObjectURL(url); }, 'image/png'); });
下载链接点击无效或触发多次的典型原因
最常踩的坑是事件绑定时机不对,或 URL.createObjectURL() 未及时释放。
- 动态创建的
<a></a>标签必须在当前用户手势(如click)上下文中触发.click(),否则 Chrome 会拦截 - 重复调用
createObjectURL而不revoke,会导致内存泄漏,后续下载可能卡死或返回空内容 - 不要在异步回调外提前创建
<a></a>并缓存——DOM 引用可能失效,且href指向的 blob URL 有生命周期 - 移动端 Safari 对
download属性完全忽略,只能靠location.href = url触发另存为,但会跳转页面;稳妥做法是用iframe模拟点击(需同源)
复合海报里嵌 SVG 图标/图表时的特殊处理
直接让 html2canvas 渲染含 <svg></svg> 的 DOM,大概率出现图标丢失或错位。SVG 不是普通图片,它需要被 rasterize 成像素才能进 canvas。
- 对简单 SVG,用
svgString.replace(/#/g, '%23')编码后转 data URL:data:image/svg+xml;charset=utf8,...,再塞进<img>标签 - 对复杂 SVG(含
<use></use>、外部 symbol),先用canvg库将其渲染到临时 canvas,再把该 canvas 绘制到主 canvas 上 - 避免在 SVG 内联样式中用 CSS 变量,
html2canvas不解析它们;改用内联style="fill: #ff0;" - 如果海报含 ECharts 或其他 Canvas 图表,别试图截图整个容器——直接调
chart.getDataURL({pixelRatio: 2})拿 base64,再用fetch+arrayBuffer()转Blob
真正难的不是拼图,而是让每层内容在不同设备、不同渲染引擎下都保持像素级一致。字体 fallback、图片解码时序、SVG 坐标系转换,任何一个环节断链,下载出来的就是张“抽象画”。











