JSFiddle - React, Tailwind, and code Playground

by Gwyn Milcote

HTML

Griffin marking calculator. For 'Shrine of the Griffin'. See how tiger, leopard and dusky genes interact to create mixed markings.

<div class='container'>

<div id='mother'>Mother: <span id='parent_info_2'><b>Chiffon</b> (Bright Amber)</span><br>
<div id='parent_coat_2' class='griffin_coat coat_adult'>Rendering...</div>
<select id='f_lm1'> 
    <option value='Lm^t' selected>Lm^t</option>
    <option value='Lm^l'>Lm^l</option>
    <option value='lm'>lm</option>
</select>
<select id='f_lm2'>
    <option value='Lm^t'>Lm^t</option>
    <option value='Lm^l' selected>Lm^l</option>
    <option value='lm'>lm</option>
</select> 
<select id='f_ag1'>
    <option value='Ag'>Ag</option>
    <option value='ag' selected>ag</option>
</select>
<select id='f_ag2'>
    <option value='Ag'>Ag</option>
    <option value='ag' selected>ag</option>
</select>
</div> 
<div id='father'>Father: <span id='parent_info_1'><b>Green</b> (Normal Green)</span><br>
<div id='parent_coat_1' class='griffin_coat coat_adult'>Rendering...</div>
<select id='m_lm1'> 
    <option value='Lm^t'>Lm^t</option>
    <option value='Lm^l'>Lm^l</option>
    <option value='lm' selected>lm</option>
</select>
<select id='m_lm2'>
    <option value='Lm^t'>Lm^t</option>
    <option value='Lm^l'>Lm^l</option>
    <option value='lm' selected>lm</option>
</select> 
<select id='m_ag1'>
    <option value='Ag' selected>Ag</option>
    <option value='ag'>ag</option>
</select>
<select id='m_ag2'>
    <option value='Ag'>Ag</option>
    <option value='ag' selected>ag</option>
</select>
</div>
<br><button id='cross'>Cross</button><br>Here are the marking shapes of potential offspring.<br>

<table id='result_table'></table>

</div>

CSS

.container {text-align:center;}
#mother, #father {border:1px solid black;padding:10px;margin:5px;border-radius:5px;display:inline-block;vertical-align:top;}

table {
    width: 100%;
    border-collapse: collapse;
    margin: 20px auto;
}

table td {
    border: 1px solid #ddd;
    padding: 5px;
    text-align: center;
}

table .empty {
    border: none;
}
table .header {
    font-weight: bold;
    background-color: #ddd;
    border: 1px solid #bbb;
}

.griffin_coat {
    position: relative;
}
.griffin_coat img {
    width: 100%;
    height: 100%;
    position: absolute;
    top: 0;
    left: 0;
}

.coat_adult {
    width: 400px;
    height: 300px;
}
.coat_teen {
    width: 400px;
    height: 300px;
}

/* Same for all stages. */
.coat_bigthumb {
    width: 200px;
    height: 150px;
}
.coat_thumb {
    width: 160px;
    height: 120px;
}
.coat_small {
    width: 120px;
    height: 90px;
}
.coat_tiny {
    width: 100px;
    height: 75px;
}


.multiply {
  mix-blend-mode: multiply;
}
.overlay {
  mix-blend-mode: soft-light;
}

/* 
    Classes for each coat colour.
    'f' means filters for black PNG images.
    'c' means background colour for divs.
    1-4 are shades: Base, Faded, Bright, Cream.
*/

.c_red_1 {background-color: #ffcc33;}
.c_red_2 {background-color: #ffcc33;}
.c_red_3 {background-color: #ffcc33;}
.c_red_4 {background-color: #ffcc33;}

/* Red, salmon, amaranth, pink */
.f_red_1 {filter: invert(13%) sepia(59%) saturate(5042%) hue-rotate(354deg) brightness(81%) contrast(90%);}
.f_red_2 {filter: invert(41%) sepia(14%) saturate(6118%) hue-rotate(337deg) brightness(92%) contrast(85%);}
.f_red_3 {filter: invert(58%) sepia(73%) saturate(483%) hue-rotate(308deg) brightness(100%) contrast(108%);}
.f_red_4 {filter: invert(86%) sepia(68%) saturate(1606%) hue-rotate(289deg) brightness(121%) contrast(102%);}

/* Orange, tan, gold, cream */
.f_orange_1 {filter: invert(38%) sepia(72%) saturate(1254%) hue-rotate(358deg) brightness(87%) contrast(98%);}
.f_orange_2...

JavaScript

const path = 'http://mythstable.epizy.com/ootg/images/';

const markNames = {
    "dusky_tigard": "Dusky Tigard",
    "dusky_tiger": "Dusky Tiger",
    "dusky_leopard": "Dusky Leopard",
    "dusky": "Dusky",
    "tiger": "Tiger",
    "leopard": "Leopard",
    "tigard": "Tigard"
};


// For assembling image HTML.
class Coat {
    constructor(sex, stage, size = ""){
        this.path = path + stage + "/";
        if(stage > 3){ this.path += sex + "/"; }
        if(size != ""){ this.path += size + "/"; } // 150px
        this.startCoat();
    }
    
    startCoat(){
        this.html = "<img src='" + this.path + "ground.png'>\n";
    }
    
    addImage(url){
        this.html += "<img src='" + this.path + url + ".png'>\n";
    }
    
    addFilter(url, css){
        this.html += "<img src='" + this.path + url + ".png' class='f_" + css + "'>\n";
    }
    
    endCoat(){
        //this.html += "<img src='" + this.path + "shading.png'>\n";
        //this.html += "<img src='" + this.path + "highlight.png'>\n";
        //this.html += "<img src='" + this.path + "line.png'>\n";
        
        this.html += "<img src='" + this.path + "shade.png' class='multiply'>\n";
        this.html += "<img src='" + this.path + "shade.png' class='overlay' style='opacity:0.3;'>\n";
        this.html += "<img src='" + this.path + "line.png'>\n";
    }
    
    output(){
        return this.html;
    }
}

// Handle the Limiter/Agelaius punnett square.
class MarkCalc {
    constructor(){
        this.mother = {sex: 2, lm1: "Lm^t", lm2: "Lm^l", ag1: "ag", ag2: "ag"};
        this.father = {sex: 1, lm1: "lm", lm2: "lm", ag1: "Ag", ag2: "ag"};
        
        this.updateParent(this.mother);
        this.updateParent(this.father);
    }
    
    orderGenes(g){
        if(g.ag1 == "ag" && g.ag2 == "Ag"){ g.ag1 = "Ag"; g.ag2 = "ag"; }
        if(g.lm1 == "lm" && g.lm2 == "Lm^t"){ g.lm1 = "Lm^t"; g.lm2 = "lm"; }
        if(g.lm1 == "lm" && g.lm2 == "Lm^l"){ g.lm1 = "Lm^l"; g.lm2 = "lm"; }
     ...