React NFT艺术生成器如何下载绘制完成的不同HTML Canvas图片
问题根因
- 全程复用同一个canvas实例:所有绘制操作都作用在同一块画布上,异步加载的图片完成绘制的时序不确定,后触发的绘制会直接覆盖前一次的画布内容
- 时序逻辑完全错误:
loadImages是异步方法,你没有等待图片加载、绘制完成就用固定1秒延迟触发下载,循环连续调用两次drawLayer时,两次的绘制操作会互相抢占画布,等1秒延迟触发下载时,画布内已经只剩最后一次绘制的结果,自然所有下载的图片都一样 - 画布操作混用API:同时用
document.getElementById('myCanvas')和React refcanvas.current获取画布实例,逻辑混乱
修复方案
核心逻辑调整
把异步加载逻辑Promise化方便控制时序,每次创建独立临时canvas绘制避免内容冲突,绘制完成后立刻触发下载,取消不可靠的固定延迟。
修复后的完整代码
// loadImages改为Promise化,方便等待执行完成 const loadImages = (sources) => { return new Promise((resolve) => { const images = {}; let loadedImages = 0; const numImages = Object.keys(sources).length; if (numImages === 0) resolve(images); for (const src in sources) { images[src] = new Image(); images[src].crossOrigin = 'anonymous'; // 跨域图片导出需要加该配置 images[src].onload = () => { if (++loadedImages >= numImages) { resolve(images); } }; images[src].src = sources[src]; } }) } const download = (canvas, index) => { const url = canvas.toDataURL("image/png"); const link = document.createElement('a'); link.download = `img_${index}.png`; link.href = url; link.click(); URL.revokeObjectURL(url); // 释放内存 } const drawLayer = async (data, index) => { // 每次创建独立临时canvas,避免绘制互相干扰 const canvas = document.createElement('canvas'); canvas.width = 230; canvas.height = 230; const context = canvas.getContext('2d'); const sources = {}; // 获取当前批次的图片地址 const objURLs = getImagesURLs(data, index); objURLs.forEach((url, i) => { const key = `image${i+1}`; sources[key] = url; }) // 等待所有图片加载完成 const images = await loadImages(sources); // 按顺序绘制所有图层 objURLs.forEach((url, i) => { if (url) { context.drawImage(images[`image${i+1}`], 0, 0, 230, 230); } }) // 绘制完成立刻下载当前画布内容 download(canvas, index); } const generateCollection = async () => { // 等待单张生成完成再生成下一张,也可改用Promise.all并行生成 for(let k = 1; k <= 2; k++){ await drawLayer(data, k); } }
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/16.6.3/umd/react.production.min.js"></script> <script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/16.6.3/umd/react-dom.production.min.js"></script> <!-- 原页面的展示用canvas可按需保留,不影响生成逻辑 --> <Button onClick={()=>generateCollection()} >生成合集</Button>
内容的提问来源于stack exchange,提问作者bilalo
相关产品推荐
相关产品推荐

