Compare commits

...

21 Commits

Author SHA1 Message Date
Vftdan 9d6f01a5e6 Merge remote-tracking branch 'origin/main' into export-options 2024-04-24 20:57:06 +02:00
ente 477e2adb31 added Johann150 to the thank you section in README.md 2024-04-24 20:12:56 +02:00
fotoente a7e4ce76d1 Merge pull request 'add direct link ability' (#12) from Johann150/neomojimixer:main into main
Reviewed-on: https://codeberg.org/fotoente/neomojimixer/pulls/12
2024-04-24 18:07:19 +00:00
fotoente 26a17cd4b4 Merge branch 'main' into main 2024-04-24 18:07:01 +00:00
Vftdan 9631a7a61a Added a download link with filename for exported images 2024-04-24 15:48:20 +02:00
Vftdan 16606663e9 Added export format & quality options 2024-04-24 15:43:56 +02:00
Johann150 90d156e3f2
output direct link for export 2024-04-23 23:31:35 +02:00
Johann150 2bd7ec3818
direct link ability
This allows to link directly to one variant of an emoji by using the
URL fragment, which will in turn be selected from the available boxes.
2024-04-23 23:31:33 +02:00
Johann150 4cc1f0d7ca
fix spacing 2024-04-23 23:31:13 +02:00
Johann150 709d211297 rename eye part image file
This totally isnt because Windows can't handle this file name and because
this is annoying when using a shell which interprets the less and greater
than signs as input/output redirection.
2024-04-23 23:30:18 +02:00
ente f2382a3d00 Added neobread emojis & links to olivybee 2024-04-23 22:45:48 +02:00
ente 3c75ccb346 added neopossum floof 2024-04-23 21:20:54 +02:00
ente 0db1f26874 added mouth_woozy.png 2024-04-23 21:12:46 +02:00
ente 6ed1d5df2b removed background colour in glasses 2024-04-23 20:53:40 +02:00
ente bbe8fdc753 added thanks to vft dan in README.md 2024-04-23 20:46:25 +02:00
fotoente d202a4f150 Merge pull request 'dev' (#11) from dev into main
Reviewed-on: https://codeberg.org/fotoente/neomojimixer/pulls/11
2024-04-23 18:36:14 +00:00
fotoente 1b58a70546 Merge pull request 'Show parts names in dropdown menu' (#9) from vftdan/neomojimixer:part-dropdown into dev
Reviewed-on: https://codeberg.org/fotoente/neomojimixer/pulls/9
2024-04-23 07:31:53 +00:00
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
50 changed files with 462 additions and 381 deletions

View File

@ -26,7 +26,7 @@ The colour of the arms is determined by the last of the three entries named `"co
The export works in a way that in the background all the parts are drawn onto a canvas and then exported into a PNG that is then displayed. Direct canvas download isn't supported on the most mobile browsers so I chosen to got that little bit longer route, to make it more easily to download any of the mixed neomojis.
## I dowoloaded it localy, but it doesn't work?!?
## I downloaded it localy, but it doesn't work?!?
Welcome to the world of Javascript!
Due to security reasons javascript that is displayed in a Brwoser can't access files diretly, so to speak on the PC the JavaScript si executed one. The solutions is relativly simple: Run a small http server locally.
@ -58,5 +58,16 @@ YES! I actually do! creating the first 95 elements took me over six hours to mak
- For creating the [neopossum emojis](https://yiff.life/@EeveeEuphoria/112039918021786980)
- For letting me use them in this project!
[olivvybee](https://honeycomb.engineer/@olivvybee)
- For creating the [neobread emojis](https://github.com/olivvybee/neobread) and [blobbee emojis](https://github.com/olivvybee/blobbee/releases/latest)
- For letting me use them in this project!
[vftdan](https://mastodon.ml/@vftdan)
- For completly rewriting the JavaScript
- Adding drowpdown menus for the part selection
[Johann150](https://genau.qwertqwefsday.eu/@Johann150)
- For the permalink functionality
You
- For any feedback, bug report or pull request to improve this project!

View File

@ -5,7 +5,6 @@
<title>Neomojimixer (BETA)</title>
<link rel="shortcut icon" href="favicon.gif" type="image/gif" />
<link rel="stylesheet" href="neomojimixer.css">
</head>
<body>
<h2>Neomojimixer (BETA)</h2>
@ -18,32 +17,49 @@
<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>
<label>
Export type:
<input type="text" id="export-mime" list="export-mime-list" value="image/png" />
<datalist id="export-mime-list">
<option value="image/png"></option>
<option value="image/webp"></option>
<option value="image/jpeg"></option>
</datalist>
</label>
<label>
<input type="checkbox" id="export-quality-enabled" />Quality:<input type="number" id="export-quality" min="0.0" max="1.0" step="0.1" value="1.0" />
</label>
</div>
</div>
<div id="stats">stats</div>
<canvas id="canvas_export" width="256" height="256" hidden name="test.png"></canvas>
<img id="imageExport" src="" hidden/>
<p id="exportSaveMessage" hidden>To save right click and choose "Save image as..."</p>
<input type="text" id="fullNeomojiName" name="" value="" readonly hidden/>
<p>
<a id="imageExportLink" download href="" target="_blank" hidden>Download</a>
</p>
<p id="exportSaveMessage" hidden>To save right click and choose "Save image as..." or click the "Download" link</p>
<a id="fullNeomojiName" hidden></a>
<p>Neomojis are from the following sources: </p>
<ul>
<li><b><a href="https://volpeon.ink/emojis/neofox/" target="_blank" class="links">Neofox</a></b> by <a href="https://is-a.wyvern.rip/@volpeon" target="_blank" class="links">Volpeon</a></li>
<li><b><a href="https://volpeon.ink/emojis/neocat/" target="_blank" class="links">Neocat</a></b> by Volpeon</li>
<li><b><a href="https://emoji-repo.absturztau.be/repo/neorat.zip" target="_blank" class="links">Neorat</a></b> by <a href="https://onemuri.nl/" target="_blank" class="links">Justje</a></li>
<li><b><a href="https://yiff.life/@EeveeEuphoria/112039918021786980" target="_blank" class="links">Neopossum</a></b> by <a href="https://yiff.life/@EeveeEuphoria" target="_blank" class="links">EeveeEuphoria</a></li>
<li><b><a href="https://github.com/olivvybee/neobread" target="_blank" class="links">Neobread</a></b> by <a href="https://honeycomb.engineer/@olivvybee" target="_blank" class="links">Olivvybee</a></li>
<li><b><a href="https://github.com/olivvybee/blobbee/releases/latest" target="_blank" class="links">Blobbee</a></b> by Olivvybee</li>
<li><b>Neoredpanda</b> by <a href="https://hat-eine.entenbru.st/@Erpel" target="_blank" class="links">Ente</a></li>
</ul>
<p>Sourcecode on Codeberg: <a href="https://codeberg.org/fotoente/neomojimixer" target="_blank">Neomojimixer</a>
<p xmlns:cc="http://creativecommons.org/ns#" >This work is licensed under <a href="https://creativecommons.org/licenses/by-nc-sa/4.0/?ref=chooser-v1" target="_blank" rel="license noopener noreferrer" style="display:inline-block;">CC BY-NC-SA 4.0<img style="height:22px!important;margin-left:3px;vertical-align:text-bottom;" src="https://mirrors.creativecommons.org/presskit/icons/cc.svg?ref=chooser-v1" alt=""><img style="height:22px!important;margin-left:3px;vertical-align:text-bottom;" src="https://mirrors.creativecommons.org/presskit/icons/by.svg?ref=chooser-v1" alt=""><img style="height:22px!important;margin-left:3px;vertical-align:text-bottom;" src="https://mirrors.creativecommons.org/presskit/icons/nc.svg?ref=chooser-v1" alt=""><img style="height:22px!important;margin-left:3px;vertical-align:text-bottom;" src="https://mirrors.creativecommons.org/presskit/icons/sa.svg?ref=chooser-v1" alt=""></a></p>
<p xmlns:cc="http://creativecommons.org/ns#" >This work is licensed under <a href="https://creativecommons.org/licenses/by-nc-sa/4.0/?ref=chooser-v1" target="_blank" rel="license noopener noreferrer" style="display:inline-block;">CC BY-NC-SA 4.0<img style="height:22px!important;margin-left:3px;vertical-align:text-bottom;" src="https://mirrors.creativecommons.org/presskit/icons/cc.svg?ref=chooser-v1" alt=""><img style="height:22px!important;margin-left:3px;vertical-align:text-bottom;" src="https://mirrors.creativecommons.org/presskit/icons/by.svg?ref=chooser-v1" alt=""><img style="height:22px!important;margin-left:3px;vertical-align:text-bottom;" src="https://mirrors.creativecommons.org/presskit/icons/nc.svg?ref=chooser-v1" alt=""><img style="height:22px!important;margin-left:3px;vertical-align:text-bottom;" src="https://mirrors.creativecommons.org/presskit/icons/sa.svg?ref=chooser-v1" alt=""></a></p>
<!-- Load in the JS as the last element or the DOM objects won't be there -->
<script src="neomojimixer.js"></script>
<script src="neomojimixer.js"></script>
</body>
</html>

View File

@ -34,20 +34,37 @@ img.arms {
width: 256px;
}
input, button, select {
box-sizing: border-box;
}
button {
width: 25px;
height: 25px;
}
button#random {
input[type="checkbox"] {
margin: 4px;
width: 17px;
height: 17px;
}
.export > label {
display: block;
width: 250px;
clear: both;
}
.export > label > input:not([type="checkbox"]) {
width: 125px;
float: right;
}
button#random, button#export {
width: 250px;
}
button#export {
width: 250px;
}
span.name {
#controls > div > .name {
display: inline-block;
width: 200px;
text-align: center;

View File

@ -1,370 +1,364 @@
//global variables
const NeomojiMixer = (function(NeomojiMixer) {
//global variables
//Arrays to hold the parts
let eyes = [];
let body = [];
let mouth = [];
let arms = [];
const color_names = [
"blue",
"lightgrey",
"orange",
"red",
"white",
"yellow",
"lightbrown",
];
let selected_color = "blue";
let color_change_callbacks = [];
//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 = [];
//shotnames for HTML elements to interact with
//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;
const canvas = document.getElementById("canvas_export");
const export_img = document.getElementById("imageExport");
const export_img_download = document.getElementById("imageExportLink");
const neomoji_name = document.getElementById("fullNeomojiName");
//shotnames for HTML elements to interact with
//Stats
const stats = document.getElementById("stats");
//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;
}
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();
// If there was a hash, restore as a direct permalink.
if (document.location.hash != "") {
loadFromHash(document.location.hash);
}
window.addEventListener("hashchange", () => {
loadFromHash(document.location.hash);
});
//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 loadFromHash(hash) {
let parts = hash
.slice(1) // the first character is always the '#' sign
.split('+');
// define a constant order for the parts to appear in the hash
const parts_order = ["body", "eyes", "mouth", "arms"];
if (parts.length == parts_order.length) {
// convert the part names to part indices
parts = parts.map((name, i) =>
Array.from(part_handlers[parts_order[i]].name_element.options).findIndex(x => x.value === name)
);
if (parts.every(x => x != -1)) {
// all part names were found
parts.forEach((part_index, i) => part_handlers[parts_order[i]].setIndex(part_index));
}
}
}
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");
let export_mime = document.getElementById("export-mime").value;
let export_options = undefined;
if (document.getElementById("export-quality-enabled").checked) {
export_options = +document.getElementById("export-quality").value;
}
ctx.clearRect(0, 0, canvas.width, canvas.height);
//Set name for the emoji to use as the image name and to show as shortcode
let name = part_handlers.body.getSelectedEntry()[0] + "_" + part_handlers.eyes.getSelectedEntry()[0] + "_" + part_handlers.mouth.getSelectedEntry()[0] + "_" + part_handlers.arms.getSelectedEntry()[0];
neomoji_name.innerText = name;
neomoji_name.href = new URL("#" + part_handlers.body.getSelectedEntry()[0] + "+" + part_handlers.eyes.getSelectedEntry()[0] + "+" + part_handlers.mouth.getSelectedEntry()[0] + "+" + part_handlers.arms.getSelectedEntry()[0], document.location.href)
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(export_mime, export_options);
export_img.src = img;
export_img_download.href = img;
export_img_download.download = name + "." + (export_mime.match(/\/(\w+)/) || ["", "png"])[1];
}
setTimeout(layerCallback, 0); //Run asynchronously
export_img.hidden = false;
export_img_download.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();

View File

@ -13,7 +13,7 @@
{"name": "devil", "url": "/parts/eyes_devil.png"},
{"name": "drowsy", "url": "/parts/eyes_drowsy.png"},
{"name": "evil", "url": "/parts/eyes_evil.png"},
{"name": ">_<", "url": "/parts/eyes_><.png"},
{"name": ">_<", "url": "/parts/eyes_squint.png"},
{"name": "cry", "url": "/parts/eyes_cry.png"},
{"name": "owo", "url": "/parts/eyes_owo.png"},
{"name": "sad", "url": "/parts/eyes_sad.png"},
@ -56,6 +56,7 @@
{"name": "neocat_mask","url": "/parts/neocat_mask.png", "color": "yellow"},
{"name": "neoredpanda","url": "/parts/neoredpanda.png", "color": "orange"},
{"name": "neopossum","url": "/parts/neopossum.png", "color": "lightgrey"},
{"name": "neopossum_floof","url": "/parts/neopossum_floof.png", "color": "lightgrey"},
{"name": "neopossum_angel","url": "/parts/neopossum_angel.png", "color": "lightgrey"},
{"name": "neopossum_foxmask","url": "/parts/neopossum_foxmask.png", "color": "lightgrey"},
{"name": "neopossum_catmask","url": "/parts/neopossum_catmask.png", "color": "lightgrey"},
@ -65,6 +66,16 @@
{"name": "neopossum_rainbow","url": "/parts/neopossum_rainbow.png", "color": "lightgrey"},
{"name": "neorat","url": "/parts/neorat.png", "color": "white"},
{"name": "neorat_frozen","url": "/parts/neorat_frozen.png", "color": "blue"},
{"name": "neobread","url": "/parts/neobread.png", "color": "lightbrown"},
{"name": "neobread_ace","url": "/parts/neobread_ace.png", "color": "lightbrown"},
{"name": "neobread_agender","url": "/parts/neobread_agender.png", "color": "lightbrown"},
{"name": "neobread_aro","url": "/parts/neobread_aro.png", "color": "lightbrown"},
{"name": "neobread_bi","url": "/parts/neobread_bi.png", "color": "lightbrown"},
{"name": "neobread_lesbian","url": "/parts/neobread_lesbian.png", "color": "lightbrown"},
{"name": "neobread_nb","url": "/parts/neobread_nb.png", "color": "lightbrown"},
{"name": "neobread_pan","url": "/parts/neobread_pan.png", "color": "lightbrown"},
{"name": "neobread_rainbow","url": "/parts/neobread_rainbow.png", "color": "lightbrown"},
{"name": "neobread_trans","url": "/parts/neobread_trans.png", "color": "lightbrown"},
{"name": "neofox","url": "/parts/neofox.png", "color": "orange"}
],
"mouth": [
@ -91,6 +102,7 @@
{"name": "nom_verified","url": "/parts/mouth_nom_verified.png"},
{"name": "nom_waffle","url": "/parts/mouth_nom_waffle.png"},
{"name": "hissing","url": "/parts/mouth_hissing.png"},
{"name": "woozy","url": "/parts/mouth_woozy.png"},
{"name": "blank", "url": "/parts/blank.png"}
],
"arms": [
@ -280,6 +292,37 @@
{"name": "book","url": "/parts/arms_book_yellow.png", "color": "yellow"},
{"name": "hold_burger","url": "/parts/arms_hold_burger_yellow.png", "color": "yellow"},
{"name": "hungry","url": "/parts/arms_hungry_yellow.png", "color": "yellow"},
{"name": "hide","url": "/parts/arms_hide_lightbrown.png", "color": "lightbrown"},
{"name": "aww","url": "/parts/arms_aww_lightbrown.png", "color": "lightbrown"},
{"name": "pleading","url": "/parts/arms_pleading_lightbrown.png", "color": "lightbrown"},
{"name": "reach","url": "/parts/arms_reach_lightbrown.png", "color": "lightbrown"},
{"name": "3c","url": "/parts/arms_3c_lightbrown.png", "color": "lightbrown"},
{"name": "facepalm","url": "/parts/arms_facepalm_lightbrown.png", "color": "lightbrown"},
{"name": "mug","url": "/parts/arms_mug_lightbrown.png", "color": "lightbrown"},
{"name": "knife","url": "/parts/arms_knife_lightbrown.png", "color": "lightbrown"},
{"name": "phone","url": "/parts/arms_phone_lightbrown.png", "color": "lightbrown"},
{"name": "fingerguns","url": "/parts/arms_fingerguns_lightbrown.png", "color": "lightbrown"},
{"name": "science","url": "/parts/arms_science_lightbrown.png", "color": "lightbrown"},
{"name": "sign_no","url": "/parts/arms_sign_no_lightbrown.png", "color": "lightbrown"},
{"name": "sign_aaa","url": "/parts/arms_sign_aaa_lightbrown.png", "color": "lightbrown"},
{"name": "sign_nya","url": "/parts/arms_sign_nya_lightbrown.png", "color": "lightbrown"},
{"name": "sign_thx","url": "/parts/arms_sign_thx_lightbrown.png", "color": "lightbrown"},
{"name": "sign_yes","url": "/parts/arms_sign_yes_lightbrown.png", "color": "lightbrown"},
{"name": "sign_yip","url": "/parts/arms_sign_yip_lightbrown.png", "color": "lightbrown"},
{"name": "sign_boobs","url": "/parts/arms_sign_boobs_lightbrown.png", "color": "lightbrown"},
{"name": "sign_butts","url": "/parts/arms_sign_butts_lightbrown.png", "color": "lightbrown"},
{"name": "sign_heart","url": "/parts/arms_heart_lightbrown.png", "color": "lightbrown"},
{"name": "solder","url": "/parts/arms_solder_lightbrown.png", "color": "lightbrown"},
{"name": "redlos","url": "/parts/arms_redlos_lightbrown.png", "color": "lightbrown"},
{"name": "think","url": "/parts/arms_think_lightbrown.png", "color": "lightbrown"},
{"name": "sweat","url": "/parts/arms_sweat_lightbrown.png", "color": "lightbrown"},
{"name": "verified","url": "/parts/arms_verified_lightbrown.png", "color": "lightbrown"},
{"name": "shocked","url": "/parts/arms_shocked_lightbrown.png", "color": "lightbrown"},
{"name": "thumbsdown","url": "/parts/arms_thumbsdown_lightbrown.png", "color": "lightbrown"},
{"name": "thumbsup","url": "/parts/arms_thumbsup_lightbrown.png", "color": "lightbrown"},
{"name": "book","url": "/parts/arms_book_lightbrown.png", "color": "lightbrown"},
{"name": "hold_burger","url": "/parts/arms_hold_burger_lightbrown.png", "color": "lightbrown"},
{"name": "hungry","url": "/parts/arms_hungry_lightbrown.png", "color": "lightbrown"},
{"name": "boop","url": "/parts/arms_boop.png", "color": ""},
{"name": "sip","url": "/parts/arms_sip.png", "color": ""},
{"name": "blank", "url": "/parts/blank.png", "color": ""}

Binary file not shown.

After

Width:  |  Height:  |  Size: 6.0 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 6.3 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 8.8 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 6.8 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 7.1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 9.7 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 9.4 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 9.4 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 12 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 9.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 6.9 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 7.0 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 7.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 7.8 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 8.9 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 9.7 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 8.3 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 8.1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 10 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 9.8 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 10 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 8.3 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 8.3 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 8.5 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 8.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 9.1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 8.0 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 6.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 6.1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 6.1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 9.0 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 7.5 KiB

After

Width:  |  Height:  |  Size: 7.0 KiB

View File

Before

Width:  |  Height:  |  Size: 4.9 KiB

After

Width:  |  Height:  |  Size: 4.9 KiB

BIN
parts/mouth_woozy.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.1 KiB

BIN
parts/neobread.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 8.3 KiB

BIN
parts/neobread_ace.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 8.8 KiB

BIN
parts/neobread_agender.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 8.7 KiB

BIN
parts/neobread_aro.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 8.6 KiB

BIN
parts/neobread_bi.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 8.6 KiB

BIN
parts/neobread_lesbian.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 8.6 KiB

BIN
parts/neobread_nb.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 8.6 KiB

BIN
parts/neobread_pan.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 8.4 KiB

BIN
parts/neobread_rainbow.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 8.6 KiB

BIN
parts/neobread_trans.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 8.6 KiB

BIN
parts/neopossum_floof.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 21 KiB