使用html2canvas下载图片时提示canvas宽高为0的DOMException错误
问题描述
使用html2canvas实现指定div转canvas后下载图片的功能时,已为待转换的目标div设置明确宽高样式,运行代码始终抛出如下错误:
DOMException: Failed to execute 'drawImage' on 'CanvasRenderingContext2D': The image argument is a canvas element with a width or height of 0
相关功能实现代码如下:
const handleDownload = () => { // 待下载的自定义div let containerImage = document.createElement('div'); containerImage.style.width = 1080 containerImage.style.height = 1080 for(let i = 0;i < 3;i++){ let tmpDiv = document.createElement('div') tmpDiv.style.width = 1080 tmpDiv.style.height = 300 tmpDiv.style.background = 'red' containerImage.appendChild(tmpDiv) } document.body.appendChild(containerImage) downloadImageFromCanvas(containerImage,12,() => console.log('1')) } // 从canvas下载图片 const downloadImageFromCanvas = (Ref, id,callback) => { html2canvas(Ref) .then(async (canvas) => { const photo = await getImage({ canvas, width: 1360, height: 675 }); const element = document.createElement('a'); const blobUrl = URL.createObjectURL(photo); element.href = blobUrl; element.download = `Image ${id}`; element.click(); callback() }) .catch((e) => console.log('error', e)); }; // canvas转换为blob const getImage = async ({ canvas, width, height, mime = 'image/png', quality = 0.8, }) => { return new Promise((resolve) => { const tmpCanvas = document.createElement('canvas'); tmpCanvas.width = width * 0.99; tmpCanvas.height = height; const ctx = tmpCanvas.getContext('2d'); ctx.drawImage( canvas, 0, 0, canvas.width, canvas.height, 0, 0, width, height ); tmpCanvas.toBlob(resolve, mime, quality); }); };
故障原因
- 样式值非法:设置width、height样式时未添加单位,部分浏览器会直接判定为非法样式规则丢弃,导致节点实际渲染宽高为0
- 调用时机错误:动态创建DOM插入页面后立即调用html2canvas,此时浏览器还未完成布局、绘制流程,html2canvas无法正确计算节点尺寸,最终生成宽高为0的canvas,传入
drawImage方法时触发报错 - 逻辑缺陷:待截图节点实际尺寸为10801080,二次绘制到新canvas时硬编码使用1360675的非等比尺寸,即使修复0宽高问题,导出的图片也会出现拉伸变形
解决方案
按以下顺序调整代码即可修复问题:
- 给所有DOM宽高样式补充
px单位,保证样式规则生效 - 将html2canvas调用延迟到下一个事件循环执行,等待浏览器完成DOM渲染后再触发截图
- 显式给html2canvas传入待截图节点的宽高参数,避免尺寸计算异常
- 修正二次绘制的尺寸参数,保持和原节点等比,避免图片拉伸;如果不需要特殊裁剪,可直接使用html2canvas输出的原始canvas导出,跳过二次绘制逻辑
- 下载完成后清理临时创建的DOM、释放生成的blob地址,避免内存泄漏和页面污染
修复后的核心代码示例:
const handleDownload = () => { let containerImage = document.createElement('div'); // 补充px单位 containerImage.style.width = '1080px' containerImage.style.height = '1080px' for(let i = 0;i < 3;i++){ let tmpDiv = document.createElement('div') tmpDiv.style.width = '1080px' tmpDiv.style.height = '300px' tmpDiv.style.background = 'red' containerImage.appendChild(tmpDiv) } document.body.appendChild(containerImage) // 延迟执行等待DOM渲染完成 setTimeout(() => { downloadImageFromCanvas(containerImage,12,() => { // 移除临时DOM document.body.removeChild(containerImage) console.log('1') }) }, 0) } const downloadImageFromCanvas = (Ref, id,callback) => { html2canvas(Ref, { // 显式指定截图尺寸 width: 1080, height: 1080, useCORS: true }) .then(async (canvas) => { // 使用等比尺寸导出,避免拉伸 const photo = await getImage({ canvas, width: 1080, height: 1080 }); const element = document.createElement('a'); const blobUrl = URL.createObjectURL(photo); element.href = blobUrl; element.download = `Image ${id}`; element.click(); // 释放blob地址 URL.revokeObjectURL(blobUrl) callback() }) .catch((e) => console.log('error', e)); };
内容的提问来源于stack exchange,提问作者Minh Huy
相关产品推荐
相关产品推荐

