I’m having an issue with Canvas when trying to create a generated collection of .png images. I can create the .png files fine but the first image or the image before is not being cleared.
index.js
const fs = require("fs"); const { createCanvas, loadImage } = require("canvas"); const canvas = createCanvas(1000,1000); const ctx = canvas.getContext("2d"); const edition = 10; const {layers,width,height} = require("./input/config.js"); const saveLayer = (_canvas, _edition) => { fs.writeFileSync(`./output/${_edition}.png`, _canvas.toBuffer("image/png")); }; const drawLayer = async (_layer, _edition) => { let element = _layer.elements[Math.floor(Math.random() * _layer.elements.length)]; const image = await loadImage(`${_layer.location}${element.fileName}`); ctx.drawImage( image, _layer.position.x, _layer.position.y, _layer.size.width, _layer.size.height ); console.log(`I created the ${_layer.name} layer, and chose element ${element.name}`); saveLayer(canvas, _edition); }; for(let i = 0; i <= edition; i++){ layers.forEach(layer => { drawLayer(layer, i); }); console.log("Creating edition " + i); };
config.js
const fs = require("fs"); const width = 1000; const height = 1000; const dir = __dirname; const rarity = [ {key: "", val: "original" }, {key: "_r", val: "rare" }, {key: "_sr", val: "super rare" }, ]; const addRarity = (_str) => { let itemRarity; rarity.forEach((r) => { if (_str.includes(r.key)){ itemRarity = r.val; } }); return itemRarity; }; const cleanName = (_str) => { let name = _str.slice(0, -4); return name; }; const getElements = (path) => { return fs .readdirSync(path) //.filter((item) => !/(^|/).|^/.|/g.test(item)) .map((i,index) => { return { id: index, name: cleanName(i), fileName: i, rarity: addRarity(i), }; }); }; const layers = [ { id: 1, name: "0", location: `${dir}/0/`, elements: getElements(`${dir}/0/`), position: {x:0, y:0}, size: {width: width, height: height}, }, { id: 2, name: "1", location: `${dir}/1/`, elements: getElements(`${dir}/1/`), position: {x:0, y:0}, size: {width: width, height: height}, }, { id: 3, name: "2", location: `${dir}/2/`, elements: getElements(`${dir}/2/`), position: {x:0, y:0}, size: {width: width, height: height}, }, { id: 4, name: "3", location: `${dir}/3/`, elements: getElements(`${dir}/3/`), position: {x:0, y:0}, size: {width: width, height: height}, }, { id: 5, name: "4", location: `${dir}/4/`, elements: getElements(`${dir}/4/`), position: {x:0, y:0}, size: {width: width, height: height}, }, ]; //console.log(layers); module.exports = {layers,width,height};
The code is working fine and I’m able to create 10 .png files.
From the images above you can see the blue hat from Image 1 is still showing in Image 2.
Edit: What I’ve tried –
ctx.beginPath(); context.clearRect(0, 0, canvas.width, canvas.height); const createNFTs = async() =>{ for(let i = 1; i <= edition; i++){ for (const layer of layers) await drawLayer(layer, i); console.log("Creating edition " + i); }; };
Final Code:
for(let i = 1; i <= edition; i++){ ctx.clearRect(0, 0, canvas.width, canvas.height); for (const layer of layers) await drawLayer(layer, i); console.log("Creating edition " + i); };
Advertisement
Answer
You have a forEach calling an async function.
forEach and async do not go together: the forEach is drawing the next image before the previous one is finished
Replace
layers.forEach(layer => { drawLayer(layer, i); });
with
for (const layer of layers) { await drawLayer(layer, i); ctx.clearRect(0, 0, canvas.width, canvas.height); }
(adding clear) and put it in an async function