龙空技术网

使用Node.js随机生成NFT图片(附源码)

luckybinary 56

前言:

眼前兄弟们对“js随机生成id”大体比较珍视,朋友们都想要剖析一些“js随机生成id”的相关资讯。那么小编在网上搜集了一些对于“js随机生成id””的相关文章,希望你们能喜欢,姐妹们快快来学习一下吧!

文章简介:使用 Node.js 的 JIMP 图形处理库,通过混合图层的方式随机产生 NFT 图片和 metadata(元数据)。

在本教程中,我们将学习如何随机生成符合 OpenSea 标准的带有 metadata 的 NFT 图片(本教程也可以适用于Solana 和其它链,只需要 metadata 满足各链的要求即可)。学习本教程后能够批量获取可以直接上传到 Ethereum 或 Polygon 链的 NFT 图片。

在动手之前需要一些额外的准备工作。我们需要绘制不同图层的插画:眼睛、嘴巴、鼻子,以及我们想要包含在 NFT 图片的不同特征图片。

重要提示:同一图层的图片应具有相同的尺寸,代码会将具有透明背景的图层进行叠加,这样就不用花精力去对齐图片了,所有图层的 PNG 文件都要和背景图片尺寸相同。

下面是我们将要构建的NFT图片示例,包含正确的图层排序,错误示例(尺寸不同),以及 27 个由代码生成的NFT图片。示例图片是我用 iPad Pro 和 Procreate APP 绘制的(我是开发人员,不是设计师,画得不好但是可以说明问题):

我们将具有同一特征的图层按照如下顺序排列,每个特征都单独导出:

1- Background2- Body3- Outfit4- Head5- Nose6- Mouth7- Eyes8- Sunglasses9- Headwear

编写 Node.js 脚本

现在我们有了要在 NFT 生成器中使用的特征图片,接下来进行编码工作。本教程假设你已经安装了 Node.js 和 NPM,并且熟悉创建 NPM 项目的基础知识。(本教程示例代码和图片可以从获取)

新建文件夹并运行 npm init(默认回车即可),然后将 Traits 文件夹复制进来。现在我们将安装我们将要使用的库:fs(文件系统)、Jimp(图像处理)、dotenv(存储 pinata认证信息)和 Pinata SDK(将生成的图片上传到 IPFS )。安装依赖库:

npm i jimp fs @pinata/sdk

接着创建脚本入口文件index.js,我们通过 while 循环100次来生成100张不同的NFT图片。maxSupply可以修改成任意数字,但要保证特征图片足够多以避免重复(或自行修改代码防止创建具有重复 metadata 的图片)。代码如下:

const initial = async () => {    var _thisIndex = 1;    const _maxSupply = 100;    while(_thisIndex <= _maxSupply) {        try{            console.log('Generating NFT '+_thisIndex);            _thisIndex++;        }catch(e){            console.error('Error while generating NFT '+_thisIndex)            console.log(e);            _thisIndex = _maxSupply + 1;        }    }}initial();

下面要编写 generator.js 文件用来处理生成规则,以保证基础代码和生成规则的解耦。generator.js 将使用特征文件来产生常见、不常见、稀有和传奇四种稀有度的变化。

我们首先在index.js的同一文件夹中创建 traits.js 文件,其中包含生成眼睛、嘴巴、头部等的方法。我们将使用 Math.random 函数并返回一个字符串。

从背景样式开始,我选择岩浆和森林背景为普通,山脉和流水为罕见,城市和雪山为稀有,银河为传奇。

我们使用的特征文件名称格式为 CATEGORY_NAME(分类_名称)。例如,流水的背景文件名为 Background_Aqua。使用同一个命名格式不仅是为了更好地管理,而且可以节省很多代码。

我还通过以下方式设置稀有度范围的常数:

const COMMON_MAX_RARITY = 50; //Starts from 1const UNCOMMON_MAX_RARITY = 75;const RARE_MAX_RARITY = 95;const LEGENDARY_MAX_RARITY = 100;const randomElement = (list) => {    const _random = Math.floor(Math.random() * list.length);    return list[_random];}const getBackground = () => {    const _random = Math.floor(Math.random() * LEGENDARY_MAX_RARITY);if(_random < COMMON_MAX_RARITY) {        return randomElement([             'Lava', 'Forest'        ]);    }else if(_random < UNCOMMON_MAX_RARITY) {        return randomElement([            'Mountain', 'Aqua'        ]);    }else if(_random < RARE_MAX_RARITY) {        return randomElement([            'Psycho', 'Snow'        ]);    }else if(_random < LEGENDARY_MAX_RARITY) {        return randomElement([            'Galaxy'        ]);    }}module.exports = {    getBackground}

现在我们再添加其余的特征。对于身体和头部,我们使用相同的函数:getBodyAndHead。这是因为我们希望身体和头部匹配,外星人的头匹配外星人的身体,僵尸的头匹配僵尸的身体,等等。

现在我们已经准备好 traits.js 文件,接下来编写 generator.js 文件,我们将使用它来实际生成图像和 metadata。由于我们将使用 IPFS 保存图片,需要用到 Pinata 来帮助我们上传图片到 IPFS。

打开 Pinata 网站: 并创建一个帐户。注册很简单,中间需要验证下电子邮件。

注意:免费 Pinata 账户的上传限制为 1GB,超过需要支付费用。

登录后,你将在屏幕右上角看到一个菜单。选择 API Keys选项。

创建一个具有 Admin 权限的 API Key,设置名称并保存 API Key和API Secret,我们将在 Node.js 脚本上使用。

创建一个包含以下内容的 .env 文件,通过 dotenv 来保护我们的认证信息:

PINATA_KEY=PINATA_API_SECRET=

接着回到 generator.js 文件,导入依赖的库并使用刚刚生成的密钥来配置 Pinata。然后还需要引用我们的项目文件夹(参见下面的 _path 变量):

require('dotenv').config();const Jimp = require('jimp');const fs = require('fs');const pinataSDK = require('@pinata/sdk');const pinata = pinataSDK(process.env.PINATA_KEY, process.env.PINATA_API_SECRET);const Traits = require('./traits');const sleep = (ms) => {    return new Promise(resolve => setTimeout(resolve, ms));}const build = async(index, onComplete) => {    const _path = '/Users/jcmacur/Documents/Projects/nftgenerator/';    onComplete();}module.exports = {    build}

在继续之前,还要修改一下 index.js 文件,使得它能在每次循环时调用 Generator 生成图片。 将 index.js 内容替换为以下内容:

const Generator = require('./generator')const initial = async () => {    var _thisIndex = 1;    const _maxSupply = 100;    while(_thisIndex <= _maxSupply) {        try{            console.log('Generating NFT '+_thisIndex);            await Generator.build(_thisIndex, () => {                _thisIndex++;            })        }catch(e){            console.error('Error while generating NFT '+_thisIndex)            console.log(e);            _thisIndex = _maxSupply + 1;        }    }}initial();

现在可以开始测试脚本了 ,执行:

node index.js

如果一切顺利,应该会看到以下输出。

在我们继续之前先快速解释一下 metadata 到底是什么,以及我们怎样以正确的方式构建它。

在以太坊上,NFT 智能合约使用 URL 来同步 token。 因此 ID为 1 的 token 将通过 baseUrl(通常是你上传的 IPFS 文件夹)+ ID 拼接成的 url 来获取对应的 metadata。

例如,如果 metadata 文件夹上传到“”,则 ID 为 1 的 NFT 将是“”。

每个 metadata 都是一个 JSON 格式的文件,没有扩展名,从 1 到 maxSupply 命名。 你也可以在 OpenSea 文档站点上阅读完整的规范,我们将使用以下结构作为例子:

{"image":"IPFS URL","name":"NFT #0","traits": [   {"trait_type":"Trait 1","value":"Blue"},   {"trait_type":"Trait 2","value":"Red"},        {"trait_type":"Trait 3","value":"Green"}, ]}

现在让我们再继续 generator.js 文件。首先解释一下代码逻辑:

按顺序加载几种图层。每次加载时会随机变化,Jimp 会将图片特征信息添加到 metadata 数组中,并将其重叠到上一层(背景图层除外)。将图像保存在自动创建的 Output/images 文件夹中。在下一个动作之前等待 20 毫秒,确保图像被保存到硬盘。创建一个只读的图像流并将图片上传到 IPFS,不用担心,使用 Pinata SDK 只需要2行代码即可。最后,保存一个没有扩展名的文件(例如,是“1”而不是“1.json”),其中包含 NFT 的名称、IPFS 图片的 URL 和特征描述。

将我们的构建方法替换为以下代码并运行。由于要循环生成 100 个 NFT 图片,可能需要几分钟时间。我们可以先测试只生成背景,确保它正常工作:

const build = async(index, onComplete) => {    const _path = '/Users/jcmacur/Documents/Projects/nftgenerator/';    var _traits = [];const background = Traits.getBackground();    const backgroundJimp = await Jimp.read(_path+'/Traits/Background/Background_'+background+'.png');    _traits.push({        'trait_type': 'Background',        'value': background    });var _composedImage = backgroundJimp;await _composedImage.write('Output/images/'+index+'.png');    await sleep(20); //We give some time for the image to be actually saved in our files    const _readableStream = await fs.createReadStream(_path + '/Output/images/'+index+'.png');    const _ipfs = await pinata.pinFileToIPFS(_readableStream);await fs.writeFileSync('Output/'+index, JSON.stringify({        "name": "My NFT #"+index,        "traits": _traits,        "image": ";+_ipfs.IpfsHash    }))onComplete();}

执行 node index.js 测试一下,应该会看到以下内容:

现在我们可以添加其余的特征,按照头部、衣服和身体。。。的顺序开始,这样才能保证显示正常。 继续在 var _composedImage = backgroundJimp; 下面添加代码:

const bodyAndHead = Traits.getBodyAndHead();    const bodyJimp = await Jimp.read(_path+'/Traits/Body/Body_'+bodyAndHead+'.png');    _traits.push({        'trait_type': 'Body',        'value': bodyAndHead    });_composedImage.blit(bodyJimp, 0, 0);const outfit = Traits.getOutfit();    const outfitJimp = await Jimp.read(_path+'/Traits/Outfit/Outfit_'+outfit+'.png');    _traits.push({        'trait_type': 'Outfit',        'value': outfit    });_composedImage.blit(outfitJimp, 0, 0);const headJimp = await Jimp.read(_path+'/Traits/Head/Head_'+bodyAndHead+'.png');    _traits.push({        'trait_type': 'Head',        'value': bodyAndHead    });_composedImage.blit(headJimp, 0, 0);

再次运行脚本,我们将看到 2 个随机的人物图片!

我们现在可以按照顺序继续添加所有其余的特征到 _composedImage.blit(headJimp, 0, 0);这行代码之后 :

const nose = Traits.getNose();    const noseJimp = await Jimp.read(_path+'/Traits/Nose/Nose_'+nose+'.png');    _traits.push({        'trait_type': 'Nose',        'value': nose    });_composedImage.blit(noseJimp, 0, 0);const mouth = Traits.getMouth();    const mouthJimp = await Jimp.read(_path+'/Traits/Mouth/Mouth_'+mouth+'.png');    _traits.push({        'trait_type': 'Mouth',        'value': mouth    });_composedImage.blit(mouthJimp, 0, 0);const eyes = Traits.getEyes();    const eyesJimp = await Jimp.read(_path+'/Traits/Eyes/Eyes_'+eyes+'.png');    _traits.push({        'trait_type': 'Eyes',        'value': eyes    });_composedImage.blit(eyesJimp, 0, 0);const sunglasses = Traits.getSunglasses();    const sunglassesJimp = await Jimp.read(_path+'/Traits/Sunglasses/Sunglasses_'+sunglasses+'.png');    _traits.push({        'trait_type': 'Sunglasses',        'value': sunglasses    });_composedImage.blit(sunglassesJimp, 0, 0);const headwear = Traits.getHeadwear();    const headwearJimp = await Jimp.read(_path+'/Traits/Headwear/Headwear_'+headwear+'.png');    _traits.push({        'trait_type': 'Headwear',        'value': headwear    });_composedImage.blit(headwearJimp, 0, 0);

代码完成!再次运行 node index.js ,生成新的随机 NFT 图片:

总结

后续还有一些工作。创建了所有 NFT图片后,还需要将 IPFS 文件夹链接到智能合约上。所以我们需要上传图片文件夹并获取对应的 URL。

首先,我们将所有没有扩展名的文件移动到一个单独的文件夹(图像文件夹不是必需的,因为 IPFS 刷新可能需要一些时间,为了能快速看到生成的结果才创建,并且上传这些图片可能会占用 Pinata 的很多空间)。一旦我们有了包含所有 metadata 的文件夹,让我们打开 并转到上传文件夹。选择我们的文件夹,为其命名,然后上传:

上传后,可以能够看到 IPFS 哈希:

你的 metadata 文件夹 URL 是 CID/。所以,一个索引为 5 的 NFT,url 是 CID/5,并且你可以打开该 URL(将 CID 替换为你的)来自己测试。

本教程的示例代码和图片可以由此下载:。

原文地址:

标签: #js随机生成id #js随机图片 #js中怎么随机产生图片