Fabric.js 透明背景导出:一次性输出的关键开关
画布应用里,导出成图是最核心的动作之一。但 透明背景导出(用户想要一个不含底色、只留元素透明底的 PNG)堪称最常见的翻车现场:导出来是黑底、白底、或者带了不该出现的背景框。这篇文章讲清楚 透明导出 到底要关掉哪几样东西。
一、为什么默认导不出透明
Fabric 的 canvas.toDataURL() 默认是 把你画布当前看到的画面 直接编码。如果你的画布里有:
- 一个白色底的
workspace(工作区矩形), - 一个带背景颜色的
background(背景层),
那么导出的"透明底"实际上会被这两个层填满颜色,结果自然是"不透明"。所以要做透明导出,核心是 先把这些有色底/工作区临时藏起来,再导出,完事再恢复 。
二、导出的正确姿势
function exportTransparent(canvas, opts = {}) {
// 记录关键层并临时隐藏
const hidden = [];
canvas.getObjects().forEach((o) => {
if (['workspace', 'background'].includes(o.id)) {
if (o.visible) { o.visible = false; hidden.push(o); }
}
});
canvas.requestRenderAll();
const dataURL = canvas.toDataURL({
format: opts.format || 'png', // 只有 png 才支持透明
multiplier: opts.multiplier || 1 / canvas.getZoom(),
});
// 恢复显示
hidden.forEach((o) => (o.visible = true));
canvas.requestRenderAll();
return dataURL;
}关键点:
- 格式必须 PNG ,JPEG 不支持 alpha,透明区域会被填成黑色。
multiplier决定分辨率,导出高清图时设为1 / 当前 zoom才等于真实尺寸。
二、还要注意透明滚花的三种来源
经验里,透明没出来,常见三种根因:
- 背景层被隐藏了吗 —— 忘了把
background设置visible=false。 - workspace 矩形填充没换 —— 那个白色矩形
fill:'#fff'还在,藏掉了透明。 - 导出区域包含了空白画布 ——
toDataURL如果不指定 region,会导出整个画布(含没内容的透明区)。通常我们应该只导"画纸对应的区域",而不是整个 canvas:
function exportWorkspaceRegion(canvas, workspace) {
const z = canvas.getZoom();
const left = (canvas.width - workspace.width * z) / 2;
const top = (canvas.height - workspace.height * z) / 2;
const width = workspace.width * z;
const height = workspace.height * z;
return canvas.toDataURL({
format: 'png',
left, top, width, height,
multiplier: 1 / z, // 还原成真实 1:1
});
}主动给出 left/top/width/height,才能真正裁剪到画纸区域,把多余的空白挡在外面。
三、透明背景下导出内容
如果画布本身设计为"透明背景 + 内容",很多实现会在导出前把 background 藏起来再 zoomAuto 一次(让画纸显示完全),导出成功后再恢复。这与上面的隐藏逻辑一致,只是多加了一步"先自适应缩放让区域完整"。
四、坑
- JPEG 导出黑底 :忘了这张——透明区域在 JPEG 中必然是
#000。要透明必须 PNG。 - 导出后 leave 白/红框 :后台 layer 被隐藏后如果忘记恢复,用户再操作时背景"消失",很吓人。务必在
try/finally或回调里恢复visible。 - 裁剪区域算错 :
left/top用(canvas.width - realWidth)/2计算,realWidth = obj.width * zoom。直接照抄乘法即可。
五、验证
做一个带白色 workspace + 背景色的画布,用 toDataURL 导出一张"透明背景 PNG";再用一张透明工作区画布导出。分别检查:产出的 PNG 是否真的带 alpha(看图工具看透底)、是否只包含画布区域、恢复后画布是否完好。三步通过,透明导出就打通。
FAQ(常见问题)
问:为什么保存成 JPEG 后透明区域变黑了? 透明在 JPEG 里没有 alpha 通道,透明区域会被填成
#000黑底。要透明必须导出 PNG;只有不需要透明才考虑 JPEG 以缩小体积。问:导出后画布里的背景/装饰层"消失"了,很吓人? 多半是导出时为了画面干净隐藏了背景层,但导出后忘了恢复
visible。把"隐藏"和"恢复"放进try/finally或回调里,确保导出完成立即点亮背景。问:怎么保证只导出工作区那一小块,不让周边留白? 用
realWidth = obj.width * zoom算好裁剪尺寸,left/top用(canvas.width - realWidth)/2居中,再toDataURL({left, top, width, height})精确取图。






