Raketenwilli: Inline-SVG → auf Style(CSS) per JS zugreifen?

Hm. Ich mach ja eigentlich keine UI mehr, habe aber noch eines meiner uralten Projekte auf dem Zettel: Mit 3 Grafiken ging das mal... xeyes für Webseiten, ein nettes Gimmik.

xeyes: Augen, die der Maus folgen...

Ich will auf die die CSS-Eigenschaften der Pupillen (Pupil_1, Pupil_2) und der darauf befindliche Reflexe PupilReflex_1, PupilReflex_2 lesend und schreibend zugreifen.

Wie zum Teufel geht das mit Javascript?

<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN"
	"http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="de" lang="de">

<head>
	<title>Eyes</title>
	<meta http-equiv="content-type" content="text/html;charset=utf-8" />
	<meta name="generator" content="Geany 1.38" />
	<style>
		#Eyes{
			display: <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN"
	"http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="de" lang="de">

<head>
	<title>-Eyes</title>
	<meta http-equiv="content-type" content="text/html;charset=utf-8" />
	<meta name="generator" content="Geany 1.38" />
	<style>
		#Eyes{
			display: block;
			width:70px;
			height:35px;
			position: absolute;
			top:0px;
			left: calc(50vw - 50px);
		}  	
	</style>

	
</head>

<body>
	<p>Hih!</p>
	<svg id="Eyes" viewBox="0 0 200 100">
	  <style>
		#Eye_1 {
			cx: 40px;
			cy: 50px;
			rx: 35px;
			ry: 40px;
			fill: none;
			stroke: yellow;
			stroke-width: 15px;
		  }
		  #Eye_2 {
			cx: 135px;
			cy: 50px;
			rx: 35px;
			ry: 40px;
			fill: none;
			stroke: red;
			stroke-width: 15px;
		  }   
		  
		  #Pupil_1 {
			cx: 40px;
			cy: 50px;
			rx: 20px;
			ry: 20px;
			fill: black;
			stroke: none;
			stroke-width: 0;
		  }       

		  #PupilReflex_1 {
			cx: 45px;
			cy: 45px;
			rx: 5px;
			ry: 5px;
			fill: white;
			stroke: gray;
			stroke-width: 3px;
		  }  
		  
		  #Pupil_2 {
			cx: 135px;
			cy: 50px;
			rx: 20px;
			ry: 20px;
			fill: black;
			stroke: none;
			stroke-width: 0;
		  }		  
		  
		  #PupilReflex_2 {
			cx: 140px;
			cy: 45px;
			rx: 5px;
			ry: 5px;
			fill: white;
			stroke: gray;
			stroke-width: 3px;
		  }
	  </style>
	  <g id="Layer1">			
	  
		  <ellipse id="Eye_1" />
		  <ellipse id="Eye_2" />
		  <ellipse id="Pupil_1" />
		  <ellipse id="Pupil_2" /> 		  
		  <ellipse id="PupilReflex_1" />
		  <ellipse id="PupilReflex_2" />  
 
	  </g>
	</svg>
<body>

(sieht so aus:)

xeyes als SVG - noch ohne Manipulation

Wenn ich versuche, die Eigenschaften zu lesen ...

<script>
		const Pupil_1  = document.getElementById("Pupil_1");
		const elementStyle= Pupil_1.style;
		const out = document.getElementById("out");
		//out.textContent = '...';
		var i = 0;
		for ( const prop in elementStyle) {
		  out.textContent += i + ".:" + prop + ": ";
		  //if ( Object.hasOwn( elementStyle, prop ) ) {
			out.textContent += elementStyle[prop];
			out.textContent += " = ";
			//out.textContent += elementStyle.getPropertyValue( elementStyle[prop] );
			//out.textContent += elementStyle.getPropertyValue( prop );
			out.textContent += elementStyle.getPropertyValue( i++ );
			out.textContent += "\n";
		  //}
		}

</script>

... erhalte ich pro Objekt eine ordentliche Menge (im Mozilla sind es 1113) Namen von Eigenschaften, aber eben keine Werte. Alles NULL.

Muss ich es wie bei den früheren Grafiken machen und wirklich jedes Element, dessen Position ich manipulieren will, einzeln ins HTML einbauen oder kann ich via JS „ins SVG greifen”?

Und, um gleich den von Seiten Gunnars zu erwartenden Einwand abzufangen:

  • Kann ich irgendwie feststellen, ob es überhaupt eine Maus (einen Mauszeiger) gibt?

akzeptierte Antworten

  1. Hallo Raketenwilli,

    irgendwie hat beim Einfügen eine Taste geprellt. Ich habe das nach bestem Wissen bereinigt, bitte schau nochmal drauf, ob es so stimmt.

    Die Stylewerte, die Du per Stylesheet setzt, bekommst Du nur mit window.getComputedStyle.

    Aber brauchst Du das wirklich? Du willst die Positionen doch aktiv ändern, da sollte man eher custom properties setzen.

    Rolf

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

      guck Dir mal dieses SVG an. Alle magischen Zahlen sind in custom properties untergebracht. Die Pupillen sind in ein g Element gekapselt, das per transform:translate verschoben werden kann. Die Verschiebung ist größenunabhängig, es wird einfach ein Wert zwischen -1 und 1 erwartet - die Skalierung erfolgt passend innerhalb des SVG mit CSS calc().

      Du musst "nur" noch das Layer1 Element finden und in dessen style-Attribut die Custom Properties --dy, --d-left und --d-right passend setzen. Hypothese ist hier, dass die y-Auslenkung in beiden Augen immer gleich ist, und externen Anforderung ist, dass die Auslenkung in den Ecken nicht zu groß wird. Vermutlich sollte dx²+dy²<=1 gelten, ist es zu groß, musst Du herunterskalieren.

      Ob eine Maus da ist, erkennst Du daran, ob Du mousemove-Events bekommst, denke ich…

      	<svg id="Eyes" viewBox="0 0 300 140">
          <style>
            :root {
              --hcenter: 150px;
              --vcenter: 70px;
              --eye-width: 35px;
              --eye-height: 40px;
              --ring-stroke: 15px;
              --pupil-size: 20px;
            }
            .eye:nth-of-type(1) {
              --eye-center: calc(var(--hcenter) - var(--ring-stroke)*0.7 - var(--eye-width));
            }
            .eye:nth-of-type(2) {
              --eye-center: calc(var(--hcenter) + var(--ring-stroke)*0.7 + var(--eye-width));
            }
       		  .eye:nth-of-type(1) { --ring-color: yellow; }
      		  .eye:nth-of-type(2) { --ring-color: red; }   
            
      
            .eye .ring {
              cx: var(--eye-center);
              cy: var(--vcenter);
              rx: var(--eye-width);
              ry: var(--eye-height);
        			fill: none;
      			  stroke-width: var(--ring-stroke);
              stroke: var(--ring-color);
            }
            .eye:nth-of-type(1) .pupil {
              transform: translate(calc(var(--d-left)*10px), calc(var(--dy)*15px));
            }
            .eye:nth-of-type(2) .pupil {
              transform: translate(calc(var(--d-right)*10px), calc(var(--dy)*15px));
            }
            .eye .pupil ellipse:nth-child(1) {
              cx: var(--eye-center);
              cy: var(--vcenter);
        			rx: var(--pupil-size);
              ry: var(--pupil-size);
      	    	fill: black; stroke: none; stroke-width: 0;
            }
            .eye .pupil ellipse:nth-child(2) {
              cx: calc(var(--pupil-size)/4 + var(--eye-center));
              cy: calc(var(--vcenter) - var(--pupil-size)/4);
        			rx: calc(var(--pupil-size)/4);
              ry: calc(var(--pupil-size)/4);
        			fill: white; stroke: gray; stroke-width: calc(var(--pupil-size)/6);
            }
      	  </style>
      	  <g id="Layer1"  style="--dy: -0.5; --d-left: 0.8; --d-right: -0.8;">
            <g class="eye">
      	  	  <ellipse class="ring" />
              <g class="pupil">
      		      <ellipse />
      		      <ellipse />
              </g>
            </g>
            <g class="eye">
      		    <ellipse class="ring" />
              <g class="pupil">
        		    <ellipse /> 		  
      	  	    <ellipse />  
              </g>
            </g>
      	  </g>
      	</svg>
      

      Rolf

      --
      sumpsi - posui - obstruxi
      1. Danke erst mal für den Tipp und den Vorschlag.

        Ich werde das durchtesten, bin aber (neben dem Beruf) zur Zeit auch damit befasst einen dummdreisten Fahraddieb an den Wickel zu bekommen. (→ mein Blog...)

        1. Hallo Raketenwilli,

          halt Dich zurück, mit Selbstjustiz machst Du Dich selbst strafbar.

          Und selbst wenn der Name stimmt, den Du ermittelt hast, verstößt Du mit der öffentlichen Nennung gegen das Persönlichkeitsrecht des Täters. Wenn Du ihm mit seiner Fahrradschloss-Beißzange eine Zahnbehandlung spendierst, ist das Körperletzung.

          Bis ihn ein Richter verurteilt hat, ist er "mutmaßlich" und als unschuldig anzusehen. Selbst wenn Du während des Diebstahls direkt neben ihm stehen und ihm ein "Ich Bin Ein Fahrraddieb" auf die Stirn tätowieren konntest.

          Deutschland ist das Land, in dem man relativ leicht ungestraft Scheiße bauen kann.

          • Kein Zeuge? Keine Strafe - Aussage gegen Aussage
          • Das Opfer verdrischt den Täter? Das Opfer wird wegen Körperverletzung belangt
          • Täter sind eine Gruppe? Fast sicher straflos, man muss ja jedem einzelnen seine eigene Tat nachweisen.

          Vorteil: Man kann relativ leicht auch selbst ungestraft Scheiße bauen.

          Ist das gut? Meh…

          Möchte ich es anders haben? Möchte ich, dass ich im Verdachtsfall verdroschen werden darf? Möchte ich, dass ich wegen einer Namens- oder Gesichtsähnlichkeit verhaftet werde und selbst beweisen muss, dass ich unschuldig bin? Wäre das gut? Meh++

          Demokratie und Rechtsstaat haben blöde Nachteile. Aber was besseres haben wir nicht.

          Und E-Bikes sind auch scheiße. Wie schwer kann es denn sein, in den Rahmen einen Sperrbolzen zu integrieren, der das Tretlager blockiert und nur durch einen vernünftigen Schlüssel geöffnet werden kann? Ich gebe ja zu, nach über 1000 Folgen LockPickingLawyer bezweifle ich, dass so etwas wie ein „vernünftiger, aber bezahlbarer Schlüssel“ jemals hergestellt wurde…

          Rolf

          --
          sumpsi - posui - obstruxi
          1. halt Dich zurück, mit Selbstjustiz machst Du Dich selbst strafbar.

            Keine Angst. Ich will mir bei der angedeuteten Zahnbehandlung mit dessen eigenem Seitenschneider ja kein AIDS holen. Dagegen hilft die Einrede der Notwehr nämlich nicht.

            • Kein Zeuge?

            Ich hab ein Video vom Diebstahl und zwei Fahrradhändler, bei denen er sich - im Abstand weniger Tage und unter Zurücklassung des eigenen Personalausweises und sodann der eigenen Krankenversicherungskarte - Ebikes zu „Probefahrten“ geben ließ - und dann mit diesen verschwand. Die Händler und deren Mitarbeiter haben ihn auf den Fotos sofort wiedererkannt, ich hab also sogar nach islamischen Recht genug (4) Zeugen. Dem Typ ist alles egal ... der geht bei diesem wiederholt dummmdreisten Vorgehen (und einem halbwegs brauchbaren Richter) auch nicht in den Knast sondern als „allgemeingefährlicher“ in die Knast-Klapse. Und ab dem Stichwort greift dann die Warnfunktion.

    2. @@Rolf B

      Die Stylewerte, die Du per Stylesheet setzt, bekommst Du nur mit window.getComputedStyle.

      Beispiel

      🖖 Живіть довго і процвітайте

      --
      Ad astra per aspera
  2. @@Raketenwilli

    		#Eye_1 {
    			cx: 40px;
    			cy: 50px;
    			rx: 35px;
    			ry: 40px;
    			fill: none;
    			stroke: yellow;
    			stroke-width: 15px;
    		  }
    

    Für wen ist das? Für dich? Oder fürs Web?

    Weder Safari noch Firefox unterstützen bislang geometry properties von SVG2.

    ☞ There are four lights! In Safari und Firefox sind 4 Lichter zu sehen, in Chromia 5.[1]

    Nachtrag: I stand corrected. Mit Längenangaben (d.h. Zahl mit Längeneinheit) tun’s Safari und Firefox auch. ☞ Part II

    via JS „ins SVG greifen”?

    Für sowas gibt’s Libraries: Snap.svg.

    Und, um gleich den von Seiten Gunnars zu erwartenden Einwand abzufangen:

    • Kann ich irgendwie feststellen, ob es überhaupt eine Maus (einen Mauszeiger) gibt?

    Per pointer media query vielleicht?

    🖖 Живіть довго і процвітайте

    --
    Ad astra per aspera

    1. TNG Chain of Command, Part II ↩︎

    1. via JS „ins SVG greifen”?

      Für sowas gibt’s Libraries: Snap.svg.

      Moin.

      Du kennst ja meinen Hang dazu, derlei kleine Aufgaben „in Vanilla“ zu proggen, um das Laden allzu all-mächtiger Libs zu meiden.

      1. @@Raketenwilli

        via JS „ins SVG greifen”?

        Für sowas gibt’s Libraries: Snap.svg.

        Oder GreenSock.

        Du kennst ja meinen Hang dazu, derlei kleine Aufgaben „in Vanilla“ zu proggen, um das Laden allzu all-mächtiger Libs zu meiden.

        Prinzipiell nicht verkehrt. Sowas wie jQuery braucht man heute nicht.

        Ich würde aber jederzeit wieder Three.js verwenden als zu versuchen, direkt mit WebGL rumzumachen.

        Interaktive SVG-Animationen könnten auch was sein, wo eine dedizierte Library durchaus sinnvoll ist.

        🖖 Живіть довго і процвітайте

        --
        Ad astra per aspera
    2. @@Gunnar Bittersmann

      • Kann ich irgendwie feststellen, ob es überhaupt eine Maus (einen Mauszeiger) gibt?

      Per pointer media query vielleicht?

      In JS dann mit window.matchMedia().

      🖖 Живіть довго і процвітайте

      --
      Ad astra per aspera
  3. Hi there,

    weil ich gerade ein ähnliches Problem hatte...

    ... erhalte ich pro Objekt eine ordentliche Menge (im Mozilla sind es 1113) Namen von Eigenschaften, aber eben keine Werte. Alles NULL.

    oft liegt das daran, daß irgendwelche Objekte, sprich Graphiken, einfach noch nicht vollständig geladen sind, wenn Javascript die Werte abfrägt. Starte Dein Javascript zu Testzwecken einmal als Funktion von window.addEventlistener('load', funktion... ...

    1. Was Du im Beitrag nicht erkennen konntest: Im Original-Quelltext war das Skript unterhalb der SVG-Graphik. Die sollte also schon geladen gewesen sein.

      Mir gehts also um den „Objekt-Zugriffspfad“ aus dem HTML in das SVG-Objekt und sodann auf dessen Kind-Objekte:

      Psedocode:

      <html>
          <alles_davor></alles_davor>
      
          <svg ID="SVG_Objekt">
              <g ID="Kind_Objekt_1"></g>
              <g ID="Kind_Objekt_2"></g>
          </svg>
      
          <alles_danach></alles_danach>
      </html>
      

      Ich will also Style-Eigenschaften von DocumentSVG_ObjektKind_Objekt_n überschreiben. (Das dann in Abhängigkeit von der Mausposition und der zu ermittelnden Position des SVG-Objekts, bzw. dessen Kindern.)

      Alternativ bliebe nur, die „Kind-Objekte“ einzeln als vollständige SVG-Grafiken zu notieren:

      <html>
          <alles_davor></alles_davor>
      
          <svg ID="SVG_Objekt1">
              <g></g>
          </svg>
      
          <svg ID="SVG_Objekt2">
              <g></g>
          </svg>
      
          <alles_danach></alles_danach>
      </html>
      

      Die könnte ich leicht adressieren und deren Eigenschaften manipulieren. Das hab ich schon so um anno 2010 herum mal gemacht…

      1. Hi there,

        Was Du im Beitrag nicht erkennen konntest: Im Original-Quelltext war das Skript unterhalb der SVG-Graphik. Die sollte also schon geladen gewesen sein.

        Ok, das muß das von mir Gesagte aber nicht notwendigerweise ausschliessen. Das Laden von Ressourcen im Browser ist ja kein linearer Vorgang (von Skripten einmal abgesehen). Ich hab nie probiert, inwieweit das browserabhängig ist, aber wenn man bspw. ein großes Bild lädt und das auch noch von einer langsamen Quelle, dann kannst Du auch mit einem unterhalb liegenden Skript die Eigenschaften dieses Bild zB mit getcomputedStyle noch nicht abfragen. Das hat mich fast in den Wahnsinn getrieben, daß getComputedStyle.height im Skript immer 0 zurückgegeben hat, obwohl die gleiche Abfrage auf der Console den richtigen Wert ausgegeben hat😉. Erst ein kleiner Hinweis mit einem ähnlichen Problem auf stackoverflow hat mich dann in die richtige Richtung geschubst...😉

        Was jetzt natürlich eingeräumterweise noch lange nicht bedeutet, daß das auch die Ursache Deines Problems ist.

        Mir gehts also um den „Objekt-Zugriffspfad“ aus dem HTML in das SVG-Objekt und sodann auf dessen Kind-Objekte:

        Psedocode:

        <html>
            <alles_davor></alles_davor>
        
            <svg ID="SVG_Objekt">
                <g ID="Kind_Objekt_1"></g>
                <g ID="Kind_Objekt_2"></g>
            </svg>
        
            <alles_danach></alles_danach>
        </html>
        

        Ich will also Style-Eigenschaften von DocumentSVG_ObjektKind_Objekt_n überschreiben. (Das dann in Abhängigkeit von der Mausposition und der zu ermittelnden Position des SVG-Objekts, bzw. dessen Kindern.)

        Das müsste eigentlich schon gehen, das sind ja nur nth-children von svg, ich seh da jetzt keinen Grund, warum die nicht selektierbar wären...

        1. Hallo klawischnigg,

          Du hast recht, was Bilddateien angeht. Die werden asynchron geladen und stehen erst nach dem load Event sicher zur Verfügung.

          Ein inline-SVG ist aber Teil des DOM vom Hauptdokument und es würde mich sehr wundern, wenn es von einem Skript, das darunter steht (oder mit defer geladen wird), nicht sofort genutzt werden kann. Ich kann das jetzt aber nicht spontan belegen…

          Rolf

          --
          sumpsi - posui - obstruxi
          1. @@Rolf B

            Ein inline-SVG ist aber Teil des DOM vom Hauptdokument und es würde mich sehr wundern, wenn es von einem Skript, das darunter steht (oder mit defer geladen wird), nicht sofort genutzt werden kann. Ich kann das jetzt aber nicht spontan belegen…

            Spontan genug?

            🖖 Живіть довго і процвітайте

            --
            Ad astra per aspera
            1. Hallo Gunnar,

              die Mathematik lehrt, dass man mit einem (Gegen-)Beispiel beweisen kann, dass eine Behauptung falsch ist.

              Für die Richtigkeit braucht man einen Beweis. Der bestünde hier in einem Link zur Spezifikation oder hilfsweise einer Codeinspektion aller SVG-fähigen Browser

              Rolf

              --
              sumpsi - posui - obstruxi
          2. Hi there,

            Du hast recht, was Bilddateien angeht. Die werden asynchron geladen und stehen erst nach dem load Event sicher zur Verfügung.

            Ein inline-SVG ist aber Teil des DOM vom Hauptdokument und es würde mich sehr wundern, wenn es von einem Skript, das darunter steht (oder mit defer geladen wird), nicht sofort genutzt werden kann. Ich kann das jetzt aber nicht spontan belegen…

            Ist vielleicht nicht ganz das Gleiche (weil es nicht im Hauptdokument steht) aber ich hab einmal mit diesem Tool eine Balkengraphik erstellt, deren Umfang ich etwas erweitern mußte, und zwar ging es darum, daß die Zahlenwerte nicht unter den Balken sondern über den Balken und zwar gleich hoch bezogen auf die Höhe der Balken stehen sollten. Da gabs den gleichen Effekt wie im vorigen Posting beschrieben - die Manipulation der SVG-Graphik gelang erst nach dem Laden der Seite...