Create interactive map with custom elements

by der_robert

HTML

<div id="map-wrapper">
  <div id="map-content"></div>
</div>

CSS

#map-content {
  position: absolute;
  width: 500px; /* oder abhängig vom Koordinatensystem */
  height: 500px;
  background-color: #0066aa;
  background-image: url('/assets/water-tile.png'); /* z. B. wellige Textur */
  background-size: 200px 200px;
  background-repeat: repeat;
  transform-origin: top left;
  border: 1px solid red;
}

.map-island {
  position: absolute;
  width: 32px;
  height: 32px;
  pointer-events: auto;
}

.map-ship,
.map-spyship {
  position: absolute;
  width: 24px;
  height: 24px;
  pointer-events: auto;
}

/* Wrack oder Sturmereignisse */
.map-event.wrack,
.map-event.storm {
  width: 24px;
  height: 24px;
  position: absolute;
}

/* Routenlinien */
.map-route {
  height: 2px;
  background: rgba(255, 255, 255, 0.5);
  position: absolute;
  transform-origin: 0 0;
}

/* Marker auf Routen */
.map-route-marker {
  width: 16px;
  height: 16px;
  position: absolute;
  pointer-events: none;
}

JavaScript

// File: mapRenderer.js (HTML-DOM-basierte Version ohne Canvas)

const wrapper = document.getElementById('map-wrapper');
const content = document.getElementById('map-content');

let scale = 1.2;
let offsetX = 0;
let offsetY = 0;

const ICONS = {
  island: 'https://i.imgur.com/geQzFeP.png',
  storm: 'https://i.imgur.com/kxXEafT.png',
  wrack: 'https://i.imgur.com/opO5psc.png',
  ship: 'https://i.imgur.com/xxeziBr.png',
  spyship: 'https://i.imgur.com/TJL9lHe.png'
};

const mapData = {
  islands: [],
  events: [],
  routes: [] // enthält nun auch ships/spyships
};

const staticMapData = {
  islands: [
    { id: 1, name: "Insel Alpha", x: 100, y: 150 },
    { id: 2, name: "Insel Beta", x: 300, y: 200 },
    { id: 3, name: "root:root", x: 423, y: 435 }
  ],
  events: [
    { type: "wrack", x: 180, y: 180 },
    { type: "storm", x: 250, y: 220 }
  ],
  routes: [
    {
      type: "ship",
      from: [100, 150],
      to: [300, 200],
      marker: [200, 175]
    },
    {
      type: "spyship",
      from: [100, 150],
      to: [423, 435],
      marker: [260, 290]
    }
  ]
};

const useStaticData = true;

function renderMap(data) {
  content.innerHTML = '';
  content.style.backgroundImage = 'url(/assets/water-texture.png)';
  content.style.backgroundSize = '256px 256px';

  const allCoords = [
    ...data.islands.map(i => [i.x, i.y]),
    ...data.events.map(e => [e.x, e.y]),
    ...data.routes.map(r => [r.from, r.to, r.marker]).flat()
  ];
  const maxX = Math.max(...allCoords.map(c => c[0])) + 50;
  const maxY = Math.max(...allCoords.map(c => c[1])) + 50;
  content.style.width = `${maxX * scale}px`;
  content.style.height = `${maxY * scale}px`;

  data.routes.forEach(r => {
    const x1 = r.from[0] * scale;
    const y1 = r.from[1] * scale;
    const x2 = r.to[0] * scale;
    const y2 = r.to[1] * scale;
    const angle = Math.atan2(y2 - y1, x2 - x1) * 180 / Math.PI;
    const length = Math.sqrt((x2...