Fabric.js导出canvas图片时,如何追加DOM textarea文本到图片下方?
实现思路
核心是不直接导出原有Fabric画布,而是新建一个页面不可见的离屏canvas作为合成容器,将原有画布内容、textarea文本按位置拼接绘制后,最终导出合成canvas的内容。
具体实现步骤:
- 计算合成画布的尺寸:宽度和原有Fabric画布保持一致,高度 = 原有画布高度 + 文本展示区预留高度(预留高度根据文本量、行高、上下内边距计算)
- 加载原有Fabric画布生成的图片资源,等图片加载完成后,将其绘制到离屏画布的上半区域(坐标(0,0),尺寸和原画布一致)
- 获取textarea的文本内容,按照指定的字体、行高、自动换行规则,从离屏画布上原画布底部的位置开始逐行绘制文本
- 走原有的下载逻辑,导出这个合成完成的离屏canvas
改造后的参考代码
直接替换原有导出逻辑即可,对页面现有Fabric画布的交互无侵入:
// 文本展示区的固定配置,可按需调整 const TEXT_CONFIG = { fontSize: 16, lineHeight: 1.5, padding: 16, // 文本区上下左右的内边距 color: "#333", maxTextWidth: width, // 文本不超出画布总宽度 } async function exportCanvasWithText(canvas, textareaValue: string) { // 1. 加载原有Fabric画布的图片资源 const originalCanvasUrl = canvas.toDataURL({ format: "png", width, height, }) const originalImg = new Image() originalImg.src = originalCanvasUrl // 等待图片加载完成,避免绘制空白 await new Promise((resolve) => { originalImg.onload = resolve }) // 2. 预计算文本排版,确定文本区需要的总高度 const measureCtx = document.createElement("canvas").getContext("2d") measureCtx.font = `${TEXT_CONFIG.fontSize}px sans-serif` const textLines = wrapText(textareaValue, measureCtx, TEXT_CONFIG.maxTextWidth - TEXT_CONFIG.padding * 2) const textAreaHeight = TEXT_CONFIG.padding * 2 + textLines.length * TEXT_CONFIG.fontSize * TEXT_CONFIG.lineHeight // 3. 创建离屏合成画布 const compositeCanvas = document.createElement("canvas") const compositeCtx = compositeCanvas.getContext("2d") compositeCanvas.width = width compositeCanvas.height = height + textAreaHeight // 4. 把原有Fabric画布内容绘制到离屏画布顶部 compositeCtx.drawImage(originalImg, 0, 0, width, height) // 5. 绘制textarea文本到离屏画布的下半区域 compositeCtx.fillStyle = TEXT_CONFIG.color compositeCtx.font = `${TEXT_CONFIG.fontSize}px sans-serif` const startY = height + TEXT_CONFIG.padding textLines.forEach((line, index) => { compositeCtx.fillText( line, TEXT_CONFIG.padding, startY + index * TEXT_CONFIG.fontSize * TEXT_CONFIG.lineHeight ) }) // 6. 复用原有下载逻辑,导出合成完成的画布 const url = compositeCanvas.toDataURL("png"); const a = document.createElement("a"); a.href = url; a.download = "download"; const clickHandler = () => { setTimeout(() => { URL.revokeObjectURL(url); a.removeEventListener("click", clickHandler); a.remove(); }, 150); }; a.addEventListener("click", clickHandler, false); a.click(); } // 工具方法:实现文本自动换行,对齐原生textarea的排版逻辑 function wrapText(text: string, ctx: CanvasRenderingContext2D, maxWidth: number) { const lines: string[] = [] let currentLine = "" for (let char of text) { const testLine = currentLine + char const testWidth = ctx.measureText(testLine).width if (testWidth > maxWidth && currentLine) { lines.push(currentLine) currentLine = char } else { currentLine = testLine } } if (currentLine) lines.push(currentLine) return lines }
关键注意事项
- 原Fabric画布生成的图片必须等待
onload加载完成后再执行绘制,否则会出现合成图缺失画布内容的问题 - 自动换行逻辑可根据实际排版需求迭代,比如需要适配中英文混排、长字符串断行时,可优化
wrapText的判断规则 - 如果需要匹配textarea的视觉效果,可在绘制文本前,先在离屏canvas的文本区域填充和textarea一致的背景色
内容的提问来源于stack exchange,提问作者renathy
相关产品推荐
相关产品推荐

