Skip to content

Commit e4c2fe4

Browse files
authored
Merge pull request #17 from HashLips/dev
Added blending modes
2 parents 8f89c3e + 0ed8128 commit e4c2fe4

6 files changed

Lines changed: 182 additions & 72 deletions

File tree

README.md

Lines changed: 78 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -101,6 +101,60 @@ const layerConfigurations = [
101101

102102
Then optionally, update your `format` size, ie the outputted image size, and the `growEditionSizeTo` on each `layerConfigurations` object, which is the amount of variation outputted.
103103

104+
If you want to play around with different blending modes, you can add a `blend: MODE.colorBurn` field to the layersOrder object. If you need a layers to have a different opacity then you can add the `opacity: 0.7` field to the layersOrder object as well. Both the `blend: MODE.colorBurn` and `opacity: 0.7` can be addes on the same layer if you want to.
105+
106+
Here is an example on how you can play around with both filter fields:
107+
108+
```js
109+
const layerConfigurations = [
110+
{
111+
growEditionSizeTo: 5,
112+
layersOrder: [
113+
{ name: "Background" },
114+
{ name: "Eyeball" },
115+
{ name: "Eye color", blend: MODE.colorBurn },
116+
{ name: "Iris" },
117+
{ name: "Shine" },
118+
{ name: "Bottom lid", blend: MODE.overlay, opacity: 0.7 },
119+
{ name: "Top lid", opacity: 0.7 },
120+
],
121+
},
122+
];
123+
```
124+
125+
Here is a list of the different blending modes that you can optionally use.
126+
127+
```js
128+
const MODE = {
129+
sourceOver: "source-over",
130+
sourceIn: "source-in",
131+
sourceOut: "source-out",
132+
sourceAtop: "source-out",
133+
destinationOver: "destination-over",
134+
destinationIn: "destination-in",
135+
destinationOut: "destination-out",
136+
destinationAtop: "destination-atop",
137+
lighter: "lighter",
138+
copy: "copy",
139+
xor: "xor",
140+
multiply: "multiply",
141+
screen: "screen",
142+
overlay: "overlay",
143+
darken: "darken",
144+
lighten: "lighten",
145+
colorDodge: "color-dodge",
146+
colorBurn: "color-burn",
147+
hardLight: "hard-light",
148+
softLight: "soft-light",
149+
difference: "difference",
150+
exclusion: "exclusion",
151+
hue: "hue",
152+
saturation: "saturation",
153+
color: "color",
154+
luminosity: "luminosity",
155+
};
156+
```
157+
104158
When you are all ready, run the following command and your outputted art will be in the `build` directory:
105159

106160
```sh
@@ -136,7 +190,28 @@ The program will output all the images in the `build` directory along with the m
136190
}
137191
```
138192

139-
That's it, you're done. Hope you create some awesome artworks with this code 👄.
193+
That's it, you're done.
194+
195+
### Printing rarity data (Experimental feature)
196+
197+
To see the percentages of each attribute across your collection, run:
198+
199+
```sh
200+
node rarityData.js
201+
```
202+
203+
The output will look something like this:
204+
205+
```sh
206+
Trait type: Bottom lid
207+
{ trait: 'High', chance: '20', occurrence: '40' }
208+
{ trait: 'Low', chance: '40', occurrence: '60' }
209+
{ trait: 'Middle', chance: '40', occurrence: '0' }
210+
211+
Trait type: Top lid
212+
{ trait: 'High', chance: '30', occurrence: '20' }
213+
{ trait: 'Low', chance: '20', occurrence: '40' }
214+
{ trait: 'Middle', chance: '50', occurrence: '40' }
215+
```
140216
141-
### Printing rarity data
142-
To see the percentages of each attribute across your collection, run `node rarityData.js`
217+
Hope you create some awesome artworks with this code 👄

package.json

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,6 @@
2121
"author": "Daniel Eugene Botha (HashLips)",
2222
"license": "MIT",
2323
"dependencies": {
24-
"all": "^0.0.0",
2524
"canvas": "^2.8.0",
2625
"sha1": "^1.1.1"
2726
}

rarityData.js

Lines changed: 51 additions & 59 deletions
Original file line numberDiff line numberDiff line change
@@ -1,92 +1,84 @@
1-
'use strict';
1+
"use strict";
22

3-
const fs = require('fs');
3+
const fs = require("fs");
44
const path = require("path");
55
const isLocal = typeof process.pkg === "undefined";
66
const basePath = isLocal ? process.cwd() : path.dirname(process.execPath);
77
const layersDir = `${basePath}/layers`;
88

9-
const {
10-
layerConfigurations
11-
} = require("./src/config.js");
9+
const { layerConfigurations } = require("./src/config.js");
1210

13-
const {
14-
getElements
15-
} = require("./src/main.js");
11+
const { getElements } = require("./src/main.js");
1612

1713
// read json data
18-
let rawdata = fs.readFileSync('build/_metadata.json');
14+
let rawdata = fs.readFileSync("build/_metadata.json");
1915
let data = JSON.parse(rawdata);
2016
let editionSize = data.length;
2117

2218
let rarityData = [];
2319

2420
// intialize layers to chart
2521
layerConfigurations.forEach((config) => {
26-
let layers = config.layersOrder;
22+
let layers = config.layersOrder;
2723

28-
layers.forEach((layer) => {
24+
layers.forEach((layer) => {
25+
// get elements for each layer
26+
let elementsForLayer = [];
27+
let elements = getElements(`${layersDir}/${layer.name}/`);
28+
elements.forEach((element) => {
29+
// just get name and weight for each element
30+
let rarityDataElement = {
31+
trait: element.name,
32+
chance: element.weight.toFixed(0),
33+
occurrence: 0, // initialize at 0
34+
};
35+
elementsForLayer.push(rarityDataElement);
36+
});
2937

30-
// get elements for each layer
31-
let elementsForLayer = [];
32-
let elements = getElements(`${layersDir}/${layer.name}/`);
33-
elements.forEach((element) => {
34-
// just get name and weight for each element
35-
let rarityDataElement = {
36-
trait: element.name,
37-
chance: element.weight.toFixed(2),
38-
occurrence: 0 // initialize at 0
39-
}
40-
elementsForLayer.push(rarityDataElement)
41-
});
42-
43-
// don't include duplicate layers
44-
if (!rarityData.includes(layer.name))
45-
{
46-
// add elements for each layer to chart
47-
rarityData[layer.name] = elementsForLayer;
48-
}
49-
});
38+
// don't include duplicate layers
39+
if (!rarityData.includes(layer.name)) {
40+
// add elements for each layer to chart
41+
rarityData[layer.name] = elementsForLayer;
42+
}
43+
});
5044
});
5145

5246
// fill up rarity chart with occurrences from metadata
5347
data.forEach((element) => {
54-
let attributes = element.attributes;
48+
let attributes = element.attributes;
5549

56-
attributes.forEach((attribute) => {
57-
let traitType = attribute.trait_type;
58-
let value = attribute.value;
50+
attributes.forEach((attribute) => {
51+
let traitType = attribute.trait_type;
52+
let value = attribute.value;
5953

60-
let rarityDataTraits = rarityData[traitType];
61-
rarityDataTraits.forEach((rarityDataTrait) => {
62-
if (rarityDataTrait.trait == value){
63-
// keep track of occurrences
64-
rarityDataTrait.occurrence++;
65-
}
66-
});
67-
});
54+
let rarityDataTraits = rarityData[traitType];
55+
rarityDataTraits.forEach((rarityDataTrait) => {
56+
if (rarityDataTrait.trait == value) {
57+
// keep track of occurrences
58+
rarityDataTrait.occurrence++;
59+
}
60+
});
61+
});
6862
});
6963

7064
// convert occurrences to percentages
7165
for (var layer in rarityData) {
72-
for (var attribute in rarityData[layer])
73-
{
74-
// convert to percentage
75-
rarityData[layer][attribute].occurrence =
76-
(rarityData[layer][attribute].occurrence / editionSize) * 100;
66+
for (var attribute in rarityData[layer]) {
67+
// convert to percentage
68+
rarityData[layer][attribute].occurrence =
69+
(rarityData[layer][attribute].occurrence / editionSize) * 100;
7770

78-
// show two decimal places in percent
79-
rarityData[layer][attribute].occurrence =
80-
rarityData[layer][attribute].occurrence.toFixed(2);
81-
}
71+
// show two decimal places in percent
72+
rarityData[layer][attribute].occurrence =
73+
rarityData[layer][attribute].occurrence.toFixed(0);
74+
}
8275
}
8376

8477
// print out rarity data
8578
for (var layer in rarityData) {
86-
console.log(`Trait type: ${layer}`)
87-
for (var trait in rarityData[layer])
88-
{
89-
console.log(rarityData[layer][trait])
90-
}
91-
console.log()
92-
}
79+
console.log(`Trait type: ${layer}`);
80+
for (var trait in rarityData[layer]) {
81+
console.log(rarityData[layer][trait]);
82+
}
83+
console.log();
84+
}

src/blendMode.js

Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,32 @@
1+
const MODE = {
2+
sourceOver: "source-over",
3+
sourceIn: "source-in",
4+
sourceOut: "source-out",
5+
sourceAtop: "source-out",
6+
destinationOver: "destination-over",
7+
destinationIn: "destination-in",
8+
destinationOut: "destination-out",
9+
destinationAtop: "destination-atop",
10+
lighter: "lighter",
11+
copy: "copy",
12+
xor: "xor",
13+
multiply: "multiply",
14+
screen: "screen",
15+
overlay: "overlay",
16+
darken: "darken",
17+
lighten: "lighten",
18+
colorDodge: "color-dodge",
19+
colorBurn: "color-burn",
20+
hardLight: "hard-light",
21+
softLight: "soft-light",
22+
difference: "difference",
23+
exclusion: "exclusion",
24+
hue: "hue",
25+
saturation: "saturation",
26+
color: "color",
27+
luminosity: "luminosity",
28+
};
29+
30+
module.exports = {
31+
MODE,
32+
};

src/config.js

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,11 @@
1+
const { MODE } = require("./blendMode.js");
12
const description =
23
"This is the description of your NFT project, remember to replace this";
34
const baseUri = "https://hashlips/nft";
45

56
const layerConfigurations = [
67
{
7-
growEditionSizeTo: 20,
8+
growEditionSizeTo: 5,
89
layersOrder: [
910
{ name: "Background" },
1011
{ name: "Eyeball" },

src/main.js

Lines changed: 19 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -71,6 +71,9 @@ const layersSetup = (layersOrder) => {
7171
id: index,
7272
name: layerObj.name,
7373
elements: getElements(`${layersDir}/${layerObj.name}/`),
74+
blendMode:
75+
layerObj["blend"] != undefined ? layerObj["blend"] : "source-over",
76+
opacity: layerObj["opacity"] != undefined ? layerObj["opacity"] : 1,
7477
}));
7578
return layers;
7679
};
@@ -124,9 +127,11 @@ const loadLayerImg = async (_layer) => {
124127
});
125128
};
126129

127-
const drawElement = (_element) => {
128-
ctx.drawImage(_element.loadedImage, 0, 0, format.width, format.height);
129-
addAttributes(_element);
130+
const drawElement = (_renderObject) => {
131+
ctx.globalAlpha = _renderObject.layer.opacity;
132+
ctx.globalCompositeOperation = _renderObject.layer.blendMode;
133+
ctx.drawImage(_renderObject.loadedImage, 0, 0, format.width, format.height);
134+
addAttributes(_renderObject);
130135
};
131136

132137
const constructLayerToDna = (_dna = [], _layers = []) => {
@@ -136,6 +141,8 @@ const constructLayerToDna = (_dna = [], _layers = []) => {
136141
);
137142
return {
138143
name: layer.name,
144+
blendMode: layer.blendMode,
145+
opacity: layer.opacity,
139146
selectedElement: selectedElement,
140147
};
141148
});
@@ -176,7 +183,11 @@ const writeMetaData = (_data) => {
176183
const saveMetaDataSingleFile = (_editionCount) => {
177184
fs.writeFileSync(
178185
`${buildDir}/${_editionCount}.json`,
179-
JSON.stringify(metadataList.find((meta) => meta.edition == _editionCount))
186+
JSON.stringify(
187+
metadataList.find((meta) => meta.edition == _editionCount),
188+
null,
189+
2
190+
)
180191
);
181192
};
182193

@@ -200,13 +211,13 @@ const startCreating = async () => {
200211
loadedElements.push(loadLayerImg(layer));
201212
});
202213

203-
await Promise.all(loadedElements).then((elementArray) => {
214+
await Promise.all(loadedElements).then((renderObjectArray) => {
204215
ctx.clearRect(0, 0, format.width, format.height);
205216
if (background.generate) {
206217
drawBackground();
207218
}
208-
elementArray.forEach((element) => {
209-
drawElement(element);
219+
renderObjectArray.forEach((renderObject) => {
220+
drawElement(renderObject);
210221
});
211222
saveImage(editionCount);
212223
addMetadata(newDna, editionCount);
@@ -232,7 +243,7 @@ const startCreating = async () => {
232243
}
233244
layerConfigIndex++;
234245
}
235-
writeMetaData(JSON.stringify(metadataList));
246+
writeMetaData(JSON.stringify(metadataList, null, 2));
236247
};
237248

238249
module.exports = { startCreating, buildSetup, getElements };

0 commit comments

Comments
 (0)