uwe gutwirth: Auf Canvas - Objekte mit Mausevents zugreifen

Hallo Expert*innen ich habe mit Canvas eine Balkengrafik programmiert und den Wert (Zahl) jeweils in den Balken geschrieben. Da die Anzahl der Balken jedoch variable ist, werden die balken ab einer Anzahl schmäler und die Beschriftung hat keinen Platz mehr. Nun würde ich gerne mit einem mouseover den Wert anzeigen. Wie kann ich auf einen Balken zugreifen?

  1. Hallo uwe,

    die erste Frage, die man sich hier stellen sollte, ist: was tut jemand, der keine Maus hat? Simuliert ein Touch-Device ein Mouseover, wenn man den Finger auf die Stelle hält? Und wenn jemand nur eine Tastatur hat? Der müsste mit der Tab Taste durch die Balken navigieren und das Popup bekommen können. Was ist mit jemandem, der einen Screenreader benötigt, um die Daten abzurufen? Wie unterstützt Du diese Kunden?

    Aber ich gehe trotzdem mal auf deine Frage ein.

    Ein Canvas enthält keine Balken, nur unterschiedlich gefärbte Pixel. Deswegen gibt es, nachdem Du gezeichnet hast, im Canvas keinen Rückbezug mehr zu deinen Daten. Den musst Du anders herstellen.

    Beispielsweise kannst Du beim Zeichnen ein Array erzeugen, in dem die Balkenpositionen stehen. Je nach Grafik reicht hier der Mittelpunkt und die Höhe, dann musst Du im mouseover die Koordinaten neu rechnen, oder du speicherst stumpf left, right, top und bottom ab, dann ist es ein einfacher Vergleich.

    Gezeichnete Balken im Canvas sind aber auch keine interaktiven Elemente und machen das Leben schwer, wenn man sich die Fragen aus dem ersten Abschnitt stellt. Und auch ohne die Accessibility-Überlegungen kannst Du Dir das Leben leichter machen, wenn Du für die Balken HTML Elemente verwendest. Hintergrund und Koordinatensystem kannst Du auf den Canvas malen. Aber wenn Du den in ein figure-Element einhüllst, das position:relative hat, kannst Du für die Balken div-Elemente verwenden, die Du mit position:absolute, left, top, width und height an die richtige Stelle bringst. Die Werte kannst Du einfach als Text hineinsetzen, und bei Bedarf auch vertikal ausrichten (writing-mode: vertical-lr; text-orientation: mixed;)

    Von diesen HTML Elementen kannst Du dann mouseover- und ähnliche Events bekommen. Du kannst ihnen auch einen Tabindex geben und damit ermöglichen, dass man sie mit Tab anspringt. Ja, Kollegen, ich weiß, div ist kein interaktives Element, aber was soll man nehmen für eine Tastaturbedienung? Ein Button oder Link ohne Ziel kommt mir fragwürdig vor.

    Du kannst natürlich auch alles auf den Canvas malen und transparente DIVs über die Balken legen, nur für die Mausinteraktion.

    Rolf

    --
    sumpsi - posui - obstruxi
    1. Hallo,

      Je nach Situation darf aber auch die Frage erlaubt sein: Warum Canvas? Ist das zwingend nötig? Die Alternative wären div Elemente, die mit position:absolute, width, height, top und left in einem figure-Element, das position:relative hat, an die gewünschte Position gebracht werden.

      bei diesem einfachen Beispiel wäre das wohl eine Alternative; wenn's graphisch etwas anspruchsvoller wird, könnte ich mir aber auch SVG vorstellen.

      Du kannst natürlich auch alles auf den Canvas malen und transparente DIVs über die Balken legen, nur für die Mausinteraktion.

      Klar, man kann sich auch ein Feingewinde ins Knie ... also ich meine: Wenn man sowieso schon mit den div-Balken hantiert, kann man sie auch gleich passend stylen und auf Canvas verzichten.

      Live long and pros healthy,
       Martin

      --
      Paradox: Wieso heißen die Dinger Kühlkörper, obwohl sie höllisch heiß werden?
    2. Hallo zusammen,

      Je nach Situation darf aber auch die Frage erlaubt sein: Warum Canvas? Ist das zwingend nötig? Die Alternative wären div Elemente, die mit position:absolute, width, height, top und left in einem figure-Element, das position:relative hat, an die gewünschte Position gebracht werden. Denen kannst Du ein data-Attribut anpappen, das direkt auf deine Daten rückverweist. Wenn Du noch ein Gitterraster brauchst oder ein beschriftetes Koordinatensystem, dann kannst Du das mit einem Canvas in den Hintergrund malen.

      Würde eher SVG einsetzen. Dann ist DOM-Scripting auf den Balken (z. B. rect-Elementen) kein Problem.

      Grüße,
      Thomas

      1. Hallo ThomasM,

        werfen die auch Events wenn die Maus drüber fliegt? Oder kann ich mit Tastatur etwas machen, um sie an-tab-bar zu machen?

        Rolf

        --
        sumpsi - posui - obstruxi
        1. Hallo Rolf,

          werfen die auch Events wenn die Maus drüber fliegt?

          ich meine, das hätte Matthias in seinem SVG-Workshop beim SELF-Treffen demonstriert. Oder habe ich das geträumt, @Matthias Scharwies?
          Leider sind wir nur im Galopp durch das Thema gesprungen, weil wir viel Zeit mit anderen Dingen verbracht hatten.

          Oder kann ich mit Tastatur etwas machen, um sie an-tab-bar zu machen?

          Dazu kann ich leider nichts sagen, aber die Frage ist interessant.

          Live long and pros healthy,
           Martin

          --
          Paradox: Wieso heißen die Dinger Kühlkörper, obwohl sie höllisch heiß werden?
        2. Hallo Rolf,

          werfen die auch Events wenn die Maus drüber fliegt? Oder kann ich mit Tastatur etwas machen, um sie an-tab-bar zu machen?

          Mouse- und Keyboard-Events gibt es seit den Anfängen.

          SVG 1.2 hat focusable="true" (false | auto) mitgebracht und tabindex kann aktuell auch im SVG-HTML-Kontext verwendet werden.

          Grüße,
          Thomas

        3. @@Rolf B

          werfen die auch Events wenn die Maus drüber fliegt? Oder kann ich mit Tastatur etwas machen, um sie an-tab-bar zu machen?

          Die Suche nach „accessible svg chart“ liefert einige Artikel.

          😷 LLAP

          --
          „Sag mir, wie Du Deine Maske trägst, und ich sage Dir, ob Du ein Idiot bist.“ —@Ann_Waeltin
        4. Servus!

          Hallo ThomasM,

          werfen die auch Events wenn die Maus drüber fliegt? Oder kann ich mit Tastatur etwas machen, um sie an-tab-bar zu machen?

          Bei SVG funktioniert :hover und :focus (bei a-Elementen als Elternelement von Grundformen oder wenn das tabindex-Attribut gesetzt ist).

          Im Wiki nur indirekt beschrieben: SVG/Tutorials/Einstieg/SVG_mit_CSS_stylen#Geometrie-Attribute

          Herzliche Grüße

          Matthias Scharwies

          --
          Einfach mal was von der ToDo-Liste auf die Was-Solls-Liste setzen.“
          1. Hallo an Alle, herzlichen Dank für die rasche Hilfe. Werde da noch einiges lernen müssen (bin 71 alt!) Aber das hält ein altes Gehirn fit. Hatte 1969 mein erstes Programm in der Sprache autocoder mit absoluter Adressierung geschrieben, erst Jahre später kam Assembler mit variabler Adressierung......

            1. Hallo,

              Hatte 1969 mein erstes Programm in der Sprache autocoder mit absoluter Adressierung geschrieben, erst Jahre später kam Assembler mit variabler Adressierung......

              autocoder hört sich an, wie "entspannt zurücklehnen und den mal machen lassen" 😀

              Gruß
              Jürgen

              1. Hallo Jürgen,

                ja, du bekommst automatisch den Maschinencode aus deinen Assembler-Mnemonics. Ganz entspannt. Brauchst keine Sprungadressen auszurechnen. Und keine Instruktionen aus Hexcodes zusammenfummeln. Oder auf einer Tafel deinen Code zusammenstecken. Genau da ist Autocoder. GAAANZ knapp hinter dieser Periode.

                Du musst dein Programm nur erstmal auf ein Formular aufschreiben und an die Lochkartenabteilung zum Stanzen geben. Dann kannst Du mit dem Stapel zum Rechenzentrum latschen und den Batch beauftragen, der es übersetzt. Dann kannst Du ein Bier trinken gehen. Am nächsten Tag kriegst Du einen Ausdruck mit dem Programmlisting und irgendwelche blöden Errors, weil Du die 1 nicht deutlich genug vom I unterschieden hast und die Dame am Lochkartenstanzer natürlich nur stanzt, was man ihr vorgibt. Selber stanzen? Achwo, das ist unter Programmiererwürde und die StanzerInnen wollen doch auch Geld verdienen. So erzählten es damals die alten Hasen in meiner Firma, die in den 60ern angefangen hatten.

                Großartig, nicht? 50er und 60er Jahre live und - hm - noch nicht in Farbe.

                Terminals? Haha. Vielleicht in den 70ern. Als Fernschreiber. Terminals mit Monitor zum Selberprogrammieren? Hahahahaha. In den 80ern. Als ich 1985 anfing, war es kurz vorher die große Innovation, dass jeder Programmierer ein Terminal bekam, und es nicht nur 2 pro Büro gab. Und einmal 20 Zeilen blättern konnte, je nach Rechnerlast, auch mal 2 Sekunden dauern.

                Rolf

                --
                sumpsi - posui - obstruxi
                1. Hallo Rolf,

                  habe heute begonnen den HTML Weg zu gehen. Bin aber gleich auf das erste Problem gestoßen. mein erster Versuch:mein erster Versuch Mein Problem: wie bekomme ich die balken an den unteren Rand der div? Wenn das gelöst ist, werde versuchen die Anzahl, Höhe, Farbe, breite der Balken mit jQuery dynamisch zu erzeugen und dann auf jeden Balken das event mouseover & click legen um diesen dann hervorzuheben und Details anzuzeigen. Mal sehen ob ich das schaffe... Danke für eure Hilfe!

                  1. Hallo uwe gutwirth,

                    mein erster Versuch:mein erster Versuch
                    Mein Problem: wie bekomme ich die balken an den unteren Rand der div?

                    Zum Beispiel durch Verzicht auf float, stattdessen display: inline-block und vertical-align: bottom. Zudem setzen der Zeilenhöhe des umgebenden Divs auf den Wert der Höhe des Bildes.

                    Außerdem brauchst du die p-Elemente innerhalb der Balken nicht, setze stattdessen, die Zeilenhöhe wieder auf 1 (ohne Einheit, ist dann gleich der Schriftgröße) und für die Balken noch text-align: center;

                    Aber ich würde auch SVG als den besseren Weg vorschlagen.

                    Bis demnächst
                    Matthias

                    --
                    Du kannst das Projekt SELFHTML unterstützen,
                    indem du bei Amazon-Einkäufen Amazon smile (Was ist das?) nutzt.
                  2. @@uwe gutwirth

                    mein erster Versuch:mein erster Versuch Mein Problem: wie bekomme ich die balken an den unteren Rand der div?

                    Nicht floaten, sondern flexen.

                    #fer0 {
                        /* position: relative; */
                        display: flex;
                        align-items: flex-end;
                    }
                    

                    Die Auskommentierung von position: relative; meint, dass das weg kann.

                    Die Breite der Balken kannst du dann mit flex angeben, dann werden sie automatisch schmaler, wenn es mehr werden:

                    .b1 {
                        /* float: left; */
                        background-color: yellow;
                        /* width: 20px; */
                        /* bottom: 0; */
                        /* vertical-align: bottom;
                        height: 100px;
                        margin-right: 5px;
                        flex: 0 1 20px;
                    }
                    

                    Willst du margin-right wirklich auch beim letzten Balken?

                    height ist nur zur Veranschauung für die Balken ohne Werte und kommt dann auch weg, nicht wahr?

                    Überhaupt ist es nicht schön, die Höhen im Markup mit style="height: 80px" anzugeben. Schreib da die Daten in einer custom property rein: style="--value: 8". Im Stylesheet dann:

                    .b1 {
                        height: calc(var(--value) * 10px);
                    }
                    

                    Dann kannst du die Skalierung an einer Stelle für alle Balken ändern. Statt --value kannst du auch einen anderen Namen nehmen, der deine Daten besser beschreibt; er muss nur mit -- anfangen.

                    und dann auf jeden Balken das event mouseover & click legen um diesen dann hervorzuheben und Details anzuzeigen.

                    Nein!! Das wird nichts. divs sind keine interaktiven Elemente. Man kann da was für Mausschubser machen, sperrt aber alle anderen Nutzer aus, die eine Webseite nicht per Maus, sondern z.B. mit Tastatur bedienen. Wenn du Aktionen ausführen willst, brauchst du <button>s.

                    😷 LLAP

                    --
                    „Sag mir, wie Du Deine Maske trägst, und ich sage Dir, ob Du ein Idiot bist.“ —@Ann_Waeltin
                  3. Hallo uwe,

                    vergiss Float.

                    Mein Vorschlag zielte auf absolute Positionierung hin, d.h. position:absolute an den Balken (wofür man dann das position:relative am Container braucht, damit sich die Koordinaten der Balken auf den Container beziehen).

                    Eine Alternative mit automatischer Positionierung ist nicht float. Statt dessen kannst Du auch die Flexbox nehmen. HTML bleibt gleich.

                    #fer0, #fer0a {
                      display: flex;
                      align-items: flex-end;
                      /* löschen position: relative; */
                      width: 350px;
                      height: 200px;
                      background-image: url(https://bizilliance.com/bilder/y0.jpg);
                      background-repeat: no-repeat;
                      opacity: 0.5;
                    }
                    .b1 {
                      /* löschen float:left; */
                      background-color:yellow;
                      /* löschen width:20px;  */
                      /* löschen bottom: 0; */
                      /* löschen vertical-align:bottom; */
                      flex: 1 0 10px;
                      height: 100px;
                      /* ändern */ margin: 0 5px;
                    }
                    .z {
                      text-align: center;
                      /* löschen!!! vertical-align: top; */
                      font-size: 0.9;
                    }
                    

                    Eine Flexbox ordnet ihre Kind-Elemente (die Flex-Items) entlang einer Hauptache an. Diese ist per Default horizontal (flex-direction:row).

                    Wie die Kind-Elemente quer zur Hauptachse platziert werden, bestimmt align-items. Es gibt flex-start, stretch, center, flex-end und mehr, siehe unser Wiki. Für "unten" brauchen wir flex-end.

                    An den Kind-Elementen gibt es die flex-Eigenschaft. Sie kombiniert flex-grow, flex-shrink und flex-basis. flex-grow:1 sagt: das Element darf sich verbreitern, wenn Platz ist. flex-shrink1: sagt: das Element darf sich verkleinern, wenn Platz fehlt. Und flex-basis ist die Breite, von der aus gegrowt und geshrinkt wird. Die Wahrheit ist noch etwas umfangreicher, guck ins Wiki.

                    Du kannst flex-grow auch auf 0 lassen, dann bleiben die Balken auf ihrer von Dir eingestellten Breite, und du behältst Leerraum. Den zu verteilen obliegt der Eigenschaft justify-content, die Du auf #fer0 setzen musst. Es gibt flex-start (Default), flex-end, center, space-between und space-around. Probier's mal aus (oder schau in unser Wiki).

                    Mit display:flex kann man auch Text in einer Box zentrieren. Weil ein Flex-Item selbst wieder eine Flexbox sein kann, könntest Du für die .b1 Element noch hinzufügen:

                    .b1 {
                      ...
                      display: flex;
                    	justify-content: center;
                    	align-items: center;
                    }
                    

                    dann wird das p Element darin im div.b1 zentriert. Was nicht bei nackigem Text im div funktioniert, insofern ist dein p schon sinnvoll. Wobei, ich glaube, das p Element möchte lieber ein span oder div sein. <p> bringt zu viel eigene Styles für den Zweck mit. Und es braucht auch keine eigene Klasse. Du kannst es im Stylesheet mit dem Selektor .b1 p, bzw. .b1 span, ansprechen (siehe CSS Kombinatoren)

                    Wie schon gesagt: Du kannst dem Element mit dem Zahlenwert drin auch noch writing-mode:vertical-lr geben. Dann steht die Zahl senkrecht 😀.

                    Spielplatz: https://jsfiddle.net/Rolf_b/etwdv18c/

                    Rolf

                    --
                    sumpsi - posui - obstruxi