JSFiddle - React, Tailwind, and code Playground
by der_robert
HTML
<div class="viewport" id="viewport">
<div class="canvas" id="canvas">
<svg class="lines" id="lines"></svg>
</div>
</div>
CSS
body {
margin: 0;
background: #0b1e2d;
color: #fff;
font-family: Arial, sans-serif;
}
.viewport {
position: absolute;
inset: 0;
overflow: auto;
}
.canvas {
position: relative;
width: 2000px;
height: 3000px;
}
svg.lines {
position: absolute;
inset: 0;
pointer-events: none;
overflow: visible;
}
.node {
position: absolute;
width: 180px;
background: rgba(0,0,0,0.6);
border: 2px solid #654321;
border-radius: 8px;
padding: 8px;
box-shadow: 0 4px 12px rgba(0,0,0,0.5);
text-align: center;
}
.node .icon {
font-size: 24px;
margin-bottom: 4px;
}
.node .name {
font-weight: bold;
margin-bottom: 4px;
}
.edge {
stroke: #c2b280;
stroke-width: 2;
fill: none;
}
JavaScript
const BUILDINGS = [
{id:1, name:'Goldmine', icon:'⚜️'},
{id:2, name:'Steinbruch', icon:'⛏️'},
{id:3, name:'Sägewerk', icon:'🪓'},
{id:4, name:'Kohlemine', icon:'🪨'},
{id:5, name:'Eisenerzmine', icon:'⛏️'},
{id:6, name:'Schmiede', icon:'⚒️'},
{id:7, name:'Kaserne', icon:'🛡️'},
{id:8, name:'Werft', icon:'🚢'},
{id:9, name:'Lagerhaus', icon:'📦'},
{id:10, name:'Steinwall', icon:'🧱'},
{id:11, name:'Akademie', icon:'🎓'},
{id:12, name:'Wachturm', icon:'🗼'},
{id:13, name:'Marktplatz', icon:'🏪'},
{id:15, name:'Stallungen', icon:'🐎'},
{id:16, name:'Belagerungswerkstatt', icon:'⚙️'},
{id:17, name:'Kirche', icon:'⛪'},
{id:18, name:'Friedhof', icon:'🪦'},
{id:19, name:'Hafen', icon:'⚓️'},
{id:20, name:'Brunnen', icon:'⛲'},
{id:21, name:'Windmühle', icon:'💨'},
];
const REQUIREMENTS = [
{from:1, to:4}, {from:3, to:4}, {from:2, to:4},
{from:4, to:5}, {from:5, to:6}, {from:6, to:7},
{from:6, to:8}, {from:8, to:10}, {from:7, to:10},
{from:2, to:10}, {from:6, to:11}, {from:1, to:11},
{from:11, to:12}, {from:7, to:12}, {from:8, to:13},
{from:9, to:13}, {from:6, to:15}, {from:7, to:15},
{from:11, to:15}, {from:6, to:16}, {from:7, to:16},
{from:15, to:16}, {from:8, to:16}, {from:8, to:19},
{from:13, to:17}, {from:17, to:18}, {from:13, to:21}
];
function computeRanks(){
const rank = new Map(BUILDINGS.map(b=>[b.id,0]));
let changed = true;
while(changed){
changed=false;
for(const e of REQUIREMENTS){
if(rank.get(e.to) <= rank.get(e.from)){
rank.set(e.to, rank.get(e.from)+1);
changed=true;
}
}
}
return rank;
}
const rank = computeRanks();
const rows = {};
for(const b of BUILDINGS){
const r = rank.get(b.id);
if(!rows[r]) rows[r]=[];
rows[r].push(b);
}
const rowGap = 200;
const colGap = 220;
const positions = {};
Object.keys(rows).forEach(r=>{
rows[r].forEach((b,i)=>{
positions[b.id] =...