Compare commits

...

4 Commits

Author SHA1 Message Date
Vftdan d3033b8912 Use `<select>` instead of `<span>` to show parts' name
Users can now select an item from dropdown menus
2024-04-23 08:48:38 +02:00
Vftdan ed6a9097ee Refactored by moving common logic into PartHandler classes 2024-04-23 04:25:13 +02:00
Vftdan 8858549de3 Moved everything under NeomojiMixer namespace 2024-04-23 01:44:31 +02:00
Vftdan dfc0870fc1 Fixed some inconsistent whitespaces 2024-04-23 01:02:02 +02:00
3 changed files with 320 additions and 367 deletions

View File

@ -18,12 +18,12 @@
<img class="arms" id="arms_img" src="" />
</div>
<div id="controls">
<div class="body"><button id="body_left" onclick="onClick_body_prev();" disabled><</button><span class="name" id="body_name">name body</span><button id="body_right" onclick="onClick_body_next();" disabled>></button></div>
<div class="eyes"><button id="eyes_left" onclick="onClick_eyes_prev();" disabled><</button><span class="name" id="eyes_name">name eyes</span><button id="eyes_right" onclick="onClick_eyes_next();" disabled>></button></div>
<div class="mouth"><button id="mouth_left" onclick="onClick_mouth_prev();" disabled><</button><span class="name" id="mouth_name">name mouth</span><button id="mouth_right" onclick="onClick_mouth_next();" disabled>></button></div>
<div class="arms"><button id="arms_left" onclick="onClick_arms_prev();" disabled><</button><span class="name" id="arms_name">name arms</span><button id="arms_right" onclick="onClick_arms_next();" disabled>></button></div>
<div class="random"><button id="random" onclick="randomize();" disabled>Random</button></div>
<div class="export"><button id="export" onclick="exportImage();" disabled>Export Image</button></div>
<div class="body"><button id="body_left" onclick="NeomojiMixer.part_handlers.body.onClickPrev();" disabled><</button><select class="name" id="body_name" onchange="NeomojiMixer.part_handlers.body.onChangeDropdown();" disabled>name body</select><button id="body_right" onclick="NeomojiMixer.part_handlers.body.onClickNext();" disabled>></button></div>
<div class="eyes"><button id="eyes_left" onclick="NeomojiMixer.part_handlers.eyes.onClickPrev();" disabled><</button><select class="name" id="eyes_name" onchange="NeomojiMixer.part_handlers.eyes.onChangeDropdown();" disabled>name eyes</select><button id="eyes_right" onclick="NeomojiMixer.part_handlers.eyes.onClickNext();" disabled>></button></div>
<div class="mouth"><button id="mouth_left" onclick="NeomojiMixer.part_handlers.mouth.onClickPrev();" disabled><</button><select class="name" id="mouth_name" onchange="NeomojiMixer.part_handlers.mouth.onChangeDropdown();" disabled>name mouth</select><button id="mouth_right" onclick="NeomojiMixer.part_handlers.mouth.onClickNext();" disabled>></button></div>
<div class="arms"><button id="arms_left" onclick="NeomojiMixer.part_handlers.arms.onClickPrev();" disabled><</button><select class="name" id="arms_name" onchange="NeomojiMixer.part_handlers.arms.onChangeDropdown();" disabled>name arms</select><button id="arms_right" onclick="NeomojiMixer.part_handlers.arms.onClickNext();" disabled>></button></div>
<div class="random"><button id="random" onclick="NeomojiMixer.randomize();" disabled>Random</button></div>
<div class="export"><button id="export" onclick="NeomojiMixer.exportImage();" disabled>Export Image</button></div>
</div>
<div id="stats">stats</div>
<canvas id="canvas_export" width="256" height="256" hidden name="test.png"></canvas>

View File

@ -47,7 +47,7 @@ button#export {
width: 250px;
}
span.name {
#controls > div > .name {
display: inline-block;
width: 200px;
text-align: center;

View File

@ -1,370 +1,323 @@
//global variables
//Arrays to hold the parts
let eyes = [];
let body = [];
let mouth = [];
let arms = [];
//FOr all the different colours of the arms there will be each a own arraz
let arms_orange = [];
let arms_blue = [];
let arms_lightgrey = [];
let arms_red = [];
let arms_white = [];
let arms_yellow = [];
//Index to easily find when to roll back to the first/last element in the list
let inex_eyes = 0;
let index_body = 0;
let index_mouth = 0;
let index_arms = 0;
let index_color = 0;
//shotnames for HTML elements to interact with
//images
const body_image = document.getElementById("body_img");
const eyes_image = document.getElementById("eyes_img");
const mouth_image = document.getElementById("mouth_img");
const arms_image = document.getElementById("arms_img");
const canvas = document.getElementById("canvas_export");
const export_img = document.getElementById("imageExport");
const neomoji_name = document.getElementById("fullNeomojiName");
//names
const body_name = document.getElementById("body_name");
const eyes_name = document.getElementById("eyes_name");
const mouth_name = document.getElementById("mouth_name");
const arms_name = document.getElementById("arms_name");
//Stats
const stats = document.getElementById("stats");
// Loading the JSON and getting all the available parts
async function getData() {
fetch('./parts.json')
.then(function(response) {
return response.json();
})
.then(function(data) {
loadParts(data);
})
.catch(function(error) {
console.log('An error occurred:', error);
});
}
function loadParts(parts) {
//Load parts into Arrays
parts.type.eyes.forEach(fillArrayEyes);
parts.type.body.forEach(fillArrayBody);
parts.type.mouth.forEach(fillArrayMouth);
parts.type.arms.forEach(fillArrayArms);
//find the indexes of each part of the corresponding color and write those into the color arrays
fillArraysArms();
//Randomize initial view
randomize();
//Show little statistic
var sum = body.length + eyes.length + mouth.length + arms.length;
var variety = body.length * eyes.length * mouth.length * arms_orange.length;
stats.innerHTML = "There are " + sum + " Elements available,<br />with " + new Intl.NumberFormat("de-DE").format(variety) + " possible combinations.";
//Activate the buttons after everything is loaded in
document.getElementById("body_left").disabled = false;
document.getElementById("body_right").disabled = false;
document.getElementById("eyes_left").disabled = false;
document.getElementById("eyes_right").disabled = false;
document.getElementById("mouth_left").disabled = false;
document.getElementById("mouth_right").disabled = false;
document.getElementById("arms_left").disabled = false;
document.getElementById("arms_right").disabled = false;
document.getElementById("random").disabled = false;
document.getElementById("export").disabled = false;
}
function fillArrayEyes(item){
let name = item.name;
let url = item.url;
eyes.push ([name, url]); //Two dimensional array, Second dimension holds name on index 0 and url at index 1
}
function fillArrayBody(item){
let name = item.name;
let url = item.url;
let color = item.color;
body.push ([name, url, color]); //Two dimensional array, Second dimension holds name on index 0 and url at index 1
}
function fillArrayMouth(item){
let name = item.name;
let url = item.url;
mouth.push ([name, url]); //Two dimensional array, Second dimension holds name on index 0 and url at index 1
}
function fillArrayArms(item){
let name = item.name;
let url = item.url;
let color = item.color;
arms.push ([name, url, color]); //Two dimensional array, Second dimension holds name on index 0 and url at index 1
}
function fillArraysArms(){
for (let i=0; i<arms.length; i++){
if (arms[i][2] == "blue" || arms[i][2] == ""){
arms_blue.push(i);
}
if (arms[i][2] == "lightgrey" || arms[i][2] == ""){
arms_lightgrey.push(i);
}
if (arms[i][2] == "orange" || arms[i][2] == ""){
arms_orange.push(i);
}
if (arms[i][2] == "red" || arms[i][2] == ""){
arms_red.push(i);
}
if (arms[i][2] == "white" || arms[i][2] == ""){
arms_white.push(i);
}
if (arms[i][2] == "yellow" || arms[i][2] == ""){
arms_yellow.push(i);
}
}
}
function onClick_body_next(){
index_body++;
if (index_body == body.length) {index_body = 0;} //check if index is too big for the array
if (body[index_body][2] == "blue"){index_arms = arms_blue[index_color];}
else if (body[index_body][2] == "lightgrey"){index_arms = arms_lightgrey[index_color];}
else if (body[index_body][2] == "orange"){index_arms = arms_orange[index_color];}
else if (body[index_body][2] == "red"){index_arms = arms_red[index_color];}
else if (body[index_body][2] == "white"){index_arms = arms_white[index_color];}
else if (body[index_body][2] == "yellow"){index_arms = arms_yellow[index_color];}
body_image.src = "." + body[index_body][1]; //Change URL of body picture
body_name.innerHTML = body[index_body][0]; //Change body name in controls
arms_image.src = "." + arms[index_arms][1]; //Change URL of arms picture
arms_name.innerHTML = arms[index_arms][0]; //Change arms name in controls
}
function onClick_body_prev(){
index_body--;
if (index_body < 0) {index_body = (body.length-1);} //check if index is too big for the array
if (body[index_body][2] == "blue"){index_arms = arms_blue[index_color];}
else if (body[index_body][2] == "lightgrey"){index_arms = arms_lightgrey[index_color];}
else if (body[index_body][2] == "orange"){index_arms = arms_orange[index_color];}
else if (body[index_body][2] == "red"){index_arms = arms_red[index_color];}
else if (body[index_body][2] == "white"){index_arms = arms_white[index_color];}
else if (body[index_body][2] == "yellow"){index_arms = arms_yellow[index_color];}
body_image.src = "." + body[index_body][1]; //Change URL of body picture
body_name.innerHTML = body[index_body][0]; //Change body name in controls
arms_image.src = "." + arms[index_arms][1]; //Change URL of arms picture
arms_name.innerHTML = arms[index_arms][0]; //Change arms name in controls
}
function onClick_eyes_next(){
index_eyes++;
if (index_eyes == eyes.length) {index_eyes = 0;} //check if index is too big for the array
eyes_image.src = "." + eyes[index_eyes][1]; //Change URL of picture
eyes_name.innerHTML = eyes[index_eyes][0]; //Change name in controls
}
function onClick_eyes_prev(){
index_eyes--;
if (index_eyes < 0) {index_eyes = (eyes.length-1);} //check if index is too big for the array
eyes_image.src = "." + eyes[index_eyes][1]; //Change URL of picture
eyes_name.innerHTML = eyes[index_eyes][0]; //Change name in controls
}
function onClick_mouth_next(){
index_mouth++;
if (index_mouth == mouth.length) {index_mouth = 0;} //check if index is too big for the array
mouth_image.src = "." + mouth[index_mouth][1]; //Change URL of picture
mouth_name.innerHTML = mouth[index_mouth][0]; //Change name in controls
}
function onClick_mouth_prev(){
index_mouth--;
if (index_mouth < 0) {index_mouth = (mouth.length-1);} //check if index is too big for the array
mouth_image.src = "." + mouth[index_mouth][1]; //Change URL of picture
mouth_name.innerHTML = mouth[index_mouth][0]; //Change name in controls
}
function onClick_arms_next(){
index_color++;
if (body[index_body][2] == "blue"){
if (index_color == arms_blue.length) {index_color = 0;}
index_arms = arms_blue[index_color];
}
else if (body[index_body][2] == "lightgrey"){
if (index_color == arms_lightgrey.length) {index_color = 0;}
index_arms = arms_lightgrey[index_color];
}
else if (body[index_body][2] == "orange"){
if (index_color == arms_orange.length) {index_color = 0;}
index_arms = arms_orange[index_color];
}
else if (body[index_body][2] == "red"){
if (index_color == arms_red.length) {index_color = 0;}
index_arms = arms_red[index_color];
}
else if (body[index_body][2] == "white"){
if (index_color == arms_white.length) {index_color = 0;}
index_arms = arms_white[index_color];
}
else if (body[index_body][2] == "yellow"){
if (index_color == arms_yellow.length) {index_color = 0;}
index_arms = arms_yellow[index_color];
}
arms_image.src = "." + arms[index_arms][1]; //Change URL of picture
arms_name.innerHTML = arms[index_arms][0]; //Change name in controls
}
function onClick_arms_prev(){
index_color--;
if (body[index_body][2] == "blue"){
if (index_color < 0) {index_color = arms_blue.length-1;}
index_arms = arms_blue[index_color];
}
else if (body[index_body][2] == "lightgrey"){
if (index_color < 0) {index_color = arms_lightgrey.length-1;}
index_arms = arms_lightgrey[index_color];
}
else if (body[index_body][2] == "orange"){
if (index_color < 0) {index_color = arms_orange.length-1;}
index_arms = arms_orange[index_color];
}
else if (body[index_body][2] == "red"){
if (index_color < 0) {index_color = arms_red.length-1;}
index_arms = arms_red[index_color];
}
else if (body[index_body][2] == "white"){
if (index_color < 0) {index_color = arms_white.length-1;}
index_arms = arms_white[index_color];
}
else if (body[index_body][2] == "yellow"){
if (index_color < 0) {index_color = arms_yellow.length-1;}
index_arms = arms_yellow[index_color];
}
arms_image.src = "." + arms[index_arms][1]; //Change URL of picture
arms_name.innerHTML = arms[index_arms][0]; //Change name in controls
}
function randomize(){ //Randomize which parts are shown
index_body = Math.floor(Math.random() * body.length);
index_eyes = Math.floor(Math.random() * eyes.length);
index_mouth = Math.floor(Math.random() * mouth.length);
index_arms = 0;
//Determine what color the body has and chose the arms color in the same way
//Basically it does a random on the arms array and returns an index number with the right color for that body
if (body[index_body][2] == "blue"){
index_color = Math.floor(Math.random() * arms_blue.length)
index_arms = arms_blue[index_color];
}
else if (body[index_body][2] == "lightgrey"){
index_color = Math.floor(Math.random() * arms_lightgrey.length)
index_arms = arms_lightgrey[index_color];
}
else if (body[index_body][2] == "orange"){
index_color = Math.floor(Math.random() * arms_orange.length)
index_arms = arms_orange[index_color];
}
else if (body[index_body][2] == "red"){
index_color = Math.floor(Math.random() * arms_red.length)
index_arms = arms_red[index_color];
}
else if (body[index_body][2] == "white"){
index_color = Math.floor(Math.random() * arms_white.length)
index_arms = arms_white[index_color];
}
else if (body[index_body][2] == "yellow"){
index_color = Math.floor(Math.random() * arms_yellow.length)
index_arms = arms_yellow[index_color];
}
body_image.src = "." + body[index_body][1];
eyes_image.src = "." + eyes[index_eyes][1];
mouth_image.src = "." + mouth[index_mouth][1];
arms_image.src = "." + arms[index_arms][1];
body_name.innerHTML = body[index_body][0];
eyes_name.innerHTML = eyes[index_eyes][0];
mouth_name.innerHTML = mouth[index_mouth][0];
arms_name.innerHTML = arms[index_arms][0];
}
function exportImage(){ //Export image so it can be saved as one PNG
let ctx=canvas.getContext("2d");
ctx.clearRect(0, 0, canvas.width, canvas.height);
neomoji_name.value = body[index_body][0] + "_" + eyes[index_eyes][0] + "_" + mouth[index_mouth][0] + "_" + arms[index_arms][0]; //Set name for the emoji to use as the image name and to show as shortcode
let body_export = new Image();
let eyes_export = new Image();
let mouth_export = new Image();
let arms_export = new Image();
body_export.src = "." + body[index_body][1];
body_export.onload = function() {
ctx.drawImage(body_export, 0, 0, 256, 256);
eyes_export.src = "." + eyes[index_eyes][1];
eyes_export.onload = function() {
ctx.drawImage(eyes_export, 0, 0, 256, 256);
mouth_export.src = "." + mouth[index_mouth][1];
mouth_export.onload = function() {
ctx.drawImage(mouth_export, 0, 0, 256, 256);
arms_export.src = "." + arms[index_arms][1];
arms_export.onload = function() {
ctx.drawImage(arms_export, 0, 0, 256, 256);
let img = canvas.toDataURL("image/png");
export_img.src = img;
}
const NeomojiMixer = (function(NeomojiMixer) {
//global variables
const color_names = [
"blue",
"lightgrey",
"orange",
"red",
"white",
"yellow",
];
let selected_color = "blue";
let color_change_callbacks = [];
//shotnames for HTML elements to interact with
const canvas = document.getElementById("canvas_export");
const export_img = document.getElementById("imageExport");
const neomoji_name = document.getElementById("fullNeomojiName");
//Stats
const stats = document.getElementById("stats");
function onColorChange() {
for (let i = 0; i < color_change_callbacks.length; i++) {
if (color_change_callbacks[i]) {
color_change_callbacks[i]();
}
}
}
function PartHandler(name) {
this.name = name;
this.entries = []; //Arrays to hold the parts
this.entry_indices = []; //Maps selected_index to entries index
this.selected_index = 0; //index_color -> arms.selected_index; index_arms -> arms.entry_indices[arms.selected_index]
this.image_element = document.getElementById(name + "_img");
this.name_element = document.getElementById(name + "_name");
this.button_left = document.getElementById(name + "_left");
this.button_right = document.getElementById(name + "_right");
}
PartHandler.prototype = {
fillArray: function(item) {
let name = item.name;
let url = item.url;
this.entries.push([name, url]); //Two dimensional array, Second dimension holds name on index 0 and url at index 1
},
fillIndices: function() {
for (let i = 0; i < this.entries.length; i++) {
this.entry_indices.push(i); //By default preserve index
}
},
getSelectedEntry: function() {
return this.entries[this.entry_indices[this.selected_index]];
},
setIndex: function(index) {
const modulo = this.entry_indices.length; //Check if index is too big for the array
if (!modulo) {
this.selected_index = 0; //Error
} else {
index %= modulo;
if (index < 0) {
index += modulo;
}
this.selected_index = index;
}
this.redraw();
},
redraw: function() {
const entry = this.getSelectedEntry();
this.image_element.src = "." + entry[1]; //Change URL of picture
//Change name in controls
this.updateOptions();
this.name_element.selectedIndex = this.selected_index;
},
onClickNext: function() {
this.setIndex(this.selected_index + 1);
},
onClickPrev: function() {
this.setIndex(this.selected_index - 1);
},
onChangeDropdown: function() {
this.setIndex(this.name_element.selectedIndex);
},
activateControls: function() {
this.button_left.disabled = false;
this.button_right.disabled = false;
this.name_element.disabled = false;
},
randomize: function() {
this.setIndex(Math.floor(Math.random() * this.entry_indices.length));
},
createExportImage: function() {
const entry = this.getSelectedEntry();
let img = new Image();
img.src = "." + entry[1];
return img;
},
updateOptions: function() {
const options = this.name_element.options;
for (let i = 0; i < this.entry_indices.length; i++) {
const index = this.entry_indices[i];
const entry = this.entries[index];
if (options.length > i && options[i].value == entry[0]) {
continue;
} else {
const option = new Option(entry[0], entry[0], false, this.selected_index == i);
if (this.name_element.length <= i) {
this.name_element.add(option);
} else {
this.name_element.remove(i);
this.name_element.add(option, i);
}
}
}
},
};
export_img.hidden = false;
neomoji_name.hidden = false;
document.getElementById("exportSaveMessage").hidden = false;
}
function ColoredPartHandler(name) {
PartHandler.call(this, name);
//For all the different colours of the arms there will be each a own array
this.colored_indices = Object.create(null);
for (let i = 0; i < color_names.length; i++) {
this.colored_indices[color_names[i]] = [];
}
this.entry_indices = this.colored_indices[selected_color];
const that = this;
color_change_callbacks.push(function() {
that.onColorChange();
});
}
ColoredPartHandler.prototype = Object.assign(Object.create(PartHandler.prototype), {
constructor: PartHandler,
fillArray: function(item) {
let name = item.name;
let url = item.url;
let color = item.color;
this.entries.push([name, url, color]); //Two dimensional array, Second dimension holds name on index 0, url at index 1, and color at index 2
},
fillIndices: function() {
for (let i = 0; i < this.entries.length; i++) {
const color = this.entries[i][2];
if (color == "") {
//All colors
for (let j in this.colored_indices) {
this.colored_indices[j].push(i);
}
} else {
const indices = this.colored_indices[color];
if (indices == undefined) {
console.log("Cannot register " + this.name + " with unknown color: " + color);
} else {
indices.push(i);
}
}
}
},
onColorChange: function() {
this.entry_indices = this.colored_indices[selected_color];
this.redraw();
},
});
function BodyPartHandler(name) {
PartHandler.call(this, name);
}
BodyPartHandler.prototype = Object.assign(Object.create(PartHandler.prototype), {
constructor: PartHandler,
fillArray: ColoredPartHandler.prototype.fillArray,
setIndex: function(index) {
const modulo = this.entry_indices.length; //Check if index is too big for the array
if (!modulo) {
this.selected_index = 0; //Error
} else {
index %= modulo;
if (index < 0) {
index += modulo;
}
this.selected_index = index;
}
this.redraw();
const entry = this.getSelectedEntry();
selected_color = entry[2]; //Global
onColorChange(); //Global
},
});
const part_handlers = {
body: new BodyPartHandler("body"),
eyes: new PartHandler("eyes"),
mouth: new PartHandler("mouth"),
arms: new ColoredPartHandler("arms"),
};
// Loading the JSON and getting all the available parts
async function getData() {
fetch('./parts.json')
.then(function(response) {
return response.json();
})
.then(function(data) {
loadParts(data);
})
.catch(function(error) {
console.log('An error occurred:', error);
});
}
function loadParts(parts) {
//Load parts into Arrays
for (const i in part_handlers) {
parts.type[i].forEach(function(p) {part_handlers[i].fillArray(p);});
}
//find the indexes of each part of the corresponding color and write those into the color arrays
for (const i in part_handlers) {
part_handlers[i].fillIndices();
}
//Randomize initial view
randomize();
//Show little statistic
var sum = 0;
var variety = 1;
for (const i in part_handlers) {
sum += part_handlers[i].entries.length;
variety *= part_handlers[i].entry_indices.length;
}
stats.innerHTML = "There are " + sum + " Elements available,<br />with " + new Intl.NumberFormat("de-DE").format(variety) + " possible combinations.";
//Activate the buttons after everything is loaded in
for (const i in part_handlers) {
part_handlers[i].activateControls();
}
document.getElementById("random").disabled = false;
document.getElementById("export").disabled = false;
}
function randomize() { //Randomize which parts are shown
for (const i in part_handlers) {
part_handlers[i].randomize();
}
}
function exportImage() { //Export image so it can be saved as one PNG
let ctx=canvas.getContext("2d");
ctx.clearRect(0, 0, canvas.width, canvas.height);
neomoji_name.value = part_handlers.body.getSelectedEntry()[0] + "_" + part_handlers.eyes.getSelectedEntry()[0] + "_" + part_handlers.mouth.getSelectedEntry()[0] + "_" + part_handlers.arms.getSelectedEntry()[0]; //Set name for the emoji to use as the image name and to show as shortcode
let export_layers = [
part_handlers.body.createExportImage(),
part_handlers.eyes.createExportImage(),
part_handlers.mouth.createExportImage(),
part_handlers.arms.createExportImage(),
];
function layerCallback() {
while (export_layers.length) {
const layer = export_layers[0];
if (!layer.complete) {
layer.onload = layerCallback;
return; //Wait to load
}
//Finished waiting
export_layers.shift()
ctx.drawImage(layer, 0, 0, 256, 256);
}
let img = canvas.toDataURL("image/png");
export_img.src = img;
}
setTimeout(layerCallback, 0); //Run asynchronously
export_img.hidden = false;
neomoji_name.hidden = false;
document.getElementById("exportSaveMessage").hidden = false;
}
NeomojiMixer.PartHandler = PartHandler;
NeomojiMixer.ColoredPartHandler = ColoredPartHandler;
NeomojiMixer.BodyPartHandler = BodyPartHandler;
NeomojiMixer.color_names = color_names;
NeomojiMixer.color_change_callbacks = color_change_callbacks;
NeomojiMixer.part_handlers = part_handlers;
NeomojiMixer.onColorChange = onColorChange;
NeomojiMixer.getData = getData;
NeomojiMixer.loadParts = loadParts;
NeomojiMixer.randomize = randomize;
NeomojiMixer.exportImage = exportImage;
NeomojiMixer.canvas = canvas;
NeomojiMixer.export_img = export_img;
NeomojiMixer.neomoji_name = neomoji_name;
NeomojiMixer.stats = stats;
Object.defineProperty(NeomojiMixer, 'selected_color', {
enumerable: true,
configurable: true,
get: function() {
return selected_color;
},
set: function(value) {
selected_color = value;
onColorChange();
},
});
return NeomojiMixer;
})(window.NeomojiMixer || {});
//Main Programm
document.getElementById("noJSmessage").hidden = true;
getData();
NeomojiMixer.getData();