HTML Canvas als Overlay relativ zu Elternelement

michi.o

Lt. Commander
Registriert
Aug. 2010
Beiträge
1.044
Hallo,

ich versuche gerade eine kleine WebApp zu programmieren, komme aber beim Layout nicht weiter. Komme aus der C#/WPF Ecke, von HTML/CSS habe ich nicht viel Ahnung.

Ich möchte 2 Bilddateien übereinander darstellen. Auf beiden Bildern soll jeweils ein Canvas als Overlay erscheinen, so dass ich dort Punkte einzeichnen kann, die per WebSocket empfangen werden.

Nur Bilder übereinander ist kein Problem. Die Bilder einfach nacheinander in ein <div> rein.

HTML:
<!DOCTYPE html>
<html>
<head>
    <meta charset="utf-8" />
    <title>Test</title>
</head>
<body>
    <h1>Test</h1>
    <div id="mylog" >Hallo Welt</div>
    <div>
        <img src="img1.png" />
    </div>
    <div>       
         <img src="img2.png" />
    </div>    
</body>

Hab dann natürlich erstmal google gefragt und bin auf StackOverflow gelandet. Da wurde in mehreren Suchergebnissen immer dasselbe erzählt, z.B. hier https://stackoverflow.com/questions/10487292/position-absolute-but-relative-to-parent

CSS Position von Eltern <div> auf relative und CSS Position von Kind auf absolute. Dann noch z-index anpassen.
Das funktioniert aber nicht. Meine 2 Bilder und die 2 Canvas landen plötzlich alle übereinander.
Im folgenden Beispielcode habe ich für Testzwecke die Bilder durch ein Canvas ersetzt.

HTML:
<!DOCTYPE html>
<html>
<head>
    <meta charset="utf-8" />
    <title>Test</title>
    <style>        
    </style>
</head>
<body>
    <h1>Test</h1>
    <div id="mylog" >Hallo Welt</div>
    <div>
        <div style="position: relative;">
            <canvas id="bild1" width="320" height="240"
                    style="position: absolute; left: 0; top: 0; z-index: 0;" />

            <canvas id="canvas1" width="320" height="240"
                    style="position: absolute; left: 0; top: 0; z-index: 1;" />
        </div>
    </div>    
    <div>
        <div style="position: relative;">
            <canvas id="bild2" width="320" height="240"
                    style="position: absolute; left: 0; top: 0; z-index: 2;" />

            <canvas id="canvas2" width="320" height="240"
                    style="position: absolute; left: 0; top: 0; z-index: 3;" />
        </div>
    </div>

    <script>
    
        var canvas = document.getElementById("bild1");
        var ctx = canvas.getContext("2d");
        ctx.fillStyle = "red";
        ctx.fillRect(0, 0, 320, 240);
        
        canvas = document.getElementById("canvas1");
        ctx = canvas.getContext("2d");
        ctx.fillStyle = "blue";
        ctx.fillRect(0, 0, 320, 120);
        
        canvas = document.getElementById("bild2");
        ctx = canvas.getContext("2d");
        ctx.fillStyle = "yellow";
        ctx.fillRect(0, 0, 320, 240);
        
        canvas = document.getElementById("canvas2");
        ctx = canvas.getContext("2d");
        ctx.fillStyle = "green";
        ctx.fillRect(0, 0, 320, 120);
    
    </script>
   
</body>

Im Browser sieht das so aus:

html2.png


Erwartet hätte ich das hier:

html1.png


Was mache ich falsch?

Absolute Positionen relativ zur gesamten Seite möchte ich nicht verwenden. Über den Bildern, wo als Platzhalter "Hallo Welt" steht, werden noch ein paar HTML Elemente hinzukommen. Dann sollen die Bilder auch automatisch weiter nach unten wandern. Die Overlays sollen immer genau über den Bildern bleiben.
 
Ein Problem wird sein, dass deine Container abseits von den absolut positionierten canvas (die dadurch keinen space beanspruchen) keinen Inhalt und somit keine Dimension haben. Diesen "Singularitäten" liegen dann quasi übeinander, womit auch alle deine absolute positioned canvas übereinander liegen.
Gib den parent divs mal width/height, dann sieht das schon anders aus, allerdings auch nicht so wie du dir das laut Skizze vorstellst. Die absoluten canvas liegen innerhalb des parent divs durch das absolute positioning übereinander, ich dachte das wäre aber eh der Plan?
 
Vielleicht ne Info was ich mit dem HTML erreichen möchte: Das erste Bild soll eine Karte von Europa zeigen, das zweite Bild darunter eine Weltkarte. Die Canvas sollten dazu benutzt werden, eine Liste von Punkten auf der jeweiligen Karte zu zeigen.
Von daher sollen die Canvas schon über dem vorherigen Element liegen, ich brauche das ganze aber zweimal hintereinander.

Das mit der Größe von den divs probier ich morgen Mal aus
 
wenn das einfach untereinander kommen soll dann vergiss position relative/absolute. untereinander ist die standardeinstellung. left/top/z-index kannst du auch weglassen.

das wird erst interessant wenn die statt scrollen durchgeklickt werden können sollen. (slider)
wenn die absolute positionierung aus dem JS Tutorial stammt dann probier mal das hier:

HTML:
<div id="mylog" >Hallo Welt</div>
  <div  style="position:relative;">>
    <canvas id="bild2" width="320" height="240" style="position:absolute;left:0;top:0;z-index:2;" />
    <img src="img1.png" style="visibility:hidden;pointer-events:none;opacity:0;" />
  </div>
  <div  style="position:relative;">>  
    <canvas id="canvas2" width="320" height="240" style="position:absolute;left:0;top:0;z-index:3;" />
    <img src="img2.png" style="visibility:hidden;pointer-events:none;opacity:0;" />
  </div>
</div>
dann erzeugen die Bilder die größe der umliegenden divs.

das lässt sich aber mit verschiedenen möglichkeiten erzeugen. die frage ist ob du nur pixelwerte oder auch relative werte für größen nutzen willst.
 
Zuletzt bearbeitet:
aha xD

also zunächst einmal würde ich das ganze CSS auslagern und mit klassen arbeiten
(beachte die active klasse, z-index immer in 10er oder 100er schritten, dann ist zwischen den schichten luft)
CSS:
.canvas-container{
  position:relative;
}
.canvas-container canvas{
  position:absolute;
  top:0;
  left:0;
  width:100%;
  height:100%;
  z-index:10;
  visibility:hidden;
  pointer-events:none;
  opacity:0;
  transition:opacity 250ms ease;
}
.canvas-container img{
  display:block;
  transition:opacity 250ms ease;
}
.canvas-container.active-canvas canvas{
  visibility:visible;
  pointer-events:all;
  opacity:1;
}
.canvas-container.active-canvas img{
  visibility:hidden;
  pointer-events:none;
  opacity:0;
}
width und height sorgen dafür das der platz vom bild genutzt wird.

dann das HTML ausräumen
HTML:
<div id="mylog" >Hallo Welt</div>
  <div class="canvas-container">
    <img src="img1.png" />
    <canvas id="canvas1" />
  </div>
  <div class="canvas-container">
    <img src="img2.png" />
    <canvas id="canvas2" />
  </div>
</div>
das ein-/ausblenden wird mit dem setzen der klasse active-canvas auf die container elemente getriggert.
ich hab das in ein setTimeout 3sek gegeben, ich nehme an das soll in ein promise nach einem ajax request.
Javascript:
setTimout(function(){
  var canvas_container = document.querySelectorAll('.canvas-container');

  canvas_container.forEach(function(element, index){
    element.classList.add('active-canvas');
  });
}, 3000);
 
Zuletzt bearbeitet:
  • Gefällt mir
Reaktionen: michi.o und floq0r
Schon mal danke für die Hilfe. Hatte mich gewundert, warum nichts passiert, bei setTimeout hat ein 'e' gefehlt.

Ganz gepasst hat es noch nicht. Ich wollte Bild und den Canvas gleichzeitig anzeigen. Der JavaScript Code hat die Bilder ausgeblendet und nur die Canvas angezeigt.

Habe einen Teil vom Code entfernt und jetzt werden beide angezeigt. Allerdings passt das Koordinatensystem vom Canvas nicht. Gibts da noch eine Möglichkeit das zu entzerren?
Die gezeichneten Rechtecke im Beispiel haben sollten 40x40 Pixel haben, werden aber mit 42x62 Pixel aufs Canvas gezeichnet. Idealerweise sollten die Pixelkoordinaten vom Canvas mit den Pixeln vom Bild übereinstimmen.

HTML:
<!DOCTYPE html>
<html>
<head>
    <meta charset="utf-8" />
    <title>Test</title>
    <style> 

.canvas-container{
  position:relative; 
  width: 320px;
  height: 240px;
}
.canvas-container canvas{
  position:absolute;
  top:0;
  left:0;
  width:100%;
  height:100%;
  z-index:10;
  pointer-events:none;   
}

.canvas-container img{
  display:block;
  width:100%;
  height:100%;
}

    </style>
</head>
<body>
    <h1>Test</h1>
 <div id="mylog" >Hallo Welt</div>
  <div class="canvas-container">
    <img src="img1.png" />
    <canvas id="canvas1" />
  </div>
  <div class="canvas-container" >
    <img src="img2.png" />
    <canvas id="canvas2" />
  </div>
</div>
</div>
    <script>   
        var myCanvas = document.getElementById("canvas1");
        var ctx = myCanvas.getContext("2d");
        ctx.fillStyle = "blue";
        ctx.fillRect(20, 20, 40, 40);
        
        var myCanvas = document.getElementById("canvas2");
        var ctx = myCanvas.getContext("2d");
        ctx.fillStyle = "green";
        ctx.fillRect(50, 100, 40, 40);   
    </script>
  
</body>
test2.png
 
Mit Pixeln arbeiten ist halt schwierig. Wenn du die Werte in % hättest...
Ansonsten nimm bei den Canvasdimensionen die Pixelwerte statt width:100%;

PS: dinge einfach mal weglassen ist ein guter diagnoseweg bei CSS ;-)
 
Zuletzt bearbeitet:
Hab jetzt eine akzeptable Lösung gefunden. Man kann per JavaScript einfach width & height vom <img> Element auslesen und dieselben Werte per Code beim Canvas setzen. Schon passt die Skalierung wieder.
 
Zurück
Oben