SVG/Farben/Markierungen

Aus SELFHTML-Wiki
< SVG‎ | Farben
Wechseln zu: Navigation, Suche

Einige Grafikobjekte - Pfade, sowie Grundformen wie Linien, Polygonzüge oder Polygone können neben Füllung und Randlinie auch Markierungen enthalten, die Start-, End- und Zwischenpunkte eines Pfades kennzeichnen.

In diesem Tutorial lernen Sie, wie Sie solche Markierungen anlegen können.

marker - ein wiederverwendbares Symbol

Das marker-Element dient dazu - wie symbol oder pattern - ein Grafikobjekt im Definitionsabschnitt anzulegen, dass dann mehrfach referenziert werden kann.

Markierungen ansehen …
  <marker id="eins" refX="1" refY="1">
    <circle cx="1" cy="1" r=".5" stroke-width=".05" />
  </marker>

  <marker id="zwei" markerWidth="22" markerHeight="22" markerUnits="userSpaceOnUse" refx="10" refy="10">
    <circle cx="10" cy="10" r="9" stroke-width="1" />
  </marker>

Das Beispiel enthält zwei marker-Elemente:

Das obere mit der id eins enthält einen sehr kleinen Kreis mit einem Radius von einer halben Einheit; die Strichstärke beträgt sogar nur 0.05. Das marker-Element enthält ein refX- sowie ein refY-Attribut mit einem Wert von 1.

Wenn die Bezugspunkte refX und refY nicht gesetzt werden, wird ein Wert von 0,0 angenommen, sodass der Kreis seinen Mittelpunkt in der linken oberen Ecke hätte und dadurch nur teilweise sichtbar wäre.

Das untere Beispiel enthält zusätzlich weitere XML-Attribute:

  • markerHeight: Höhe des Viewports des marker-Elements
    Wenn die Markierung aus der Strichbreite herausragen soll, muss dieser Wert gesetzt werden!
  • markerWidth: Breite des Viewports des marker-Elements
  • markerUnits: legt die Berechnungsgrundlage für die weiteren Werte fest:
    • strokeWidth: bezieht sich auf die Dicke der Randlinien
    • userSpaceOnUse: legt fest, dass die "normalen Pixel" (dimensionslose Einheiten) gelten
  • refX: Bezugspunkte des marker-Elements
  • refY: Bezugspunkte des marker-Elements

Gegenüber dem alten Beispiel aus 2015 wird im aktuellen Beispiel die stroke-width des grauen Pfads animiert. Die Markierungen des oberen Pfads passen sich aufgrund des impliziten markerUnits="strokeWidth" nahtlos an - beim unteren Pfad mit markerUnits="userSpaceOnUse" müssten die Werte bei jeder Änderung der Strichstärke entsprechend justiert werden.




Das obere Beispiel diente nur zu einer ersten Betrachtung. Ihren eigentlichen Nutzen entwickeln Markierungen bei Diagrammen, wo z. B. die X-Achsen Striche entlang der Werte erhält oder die einzelnen Wende- und Scheitel-Punkte eines Liniendiagramms gekennzeichnet werden können. Dies kann man mit einzelnen SVG-Elementen erledigen - eleganter ist aber die Verwendung von Markierungen:

Markierungen referenzieren

In einem Diagramm sollen Start-, Zwischen- und Endpunkte eines Polygonzugs gekennzeichnet werden.

Dazu legen wir nun drei Markierugnen an. Genau wie bei symbol können dies beliebig viele Kindelemente sein.

Anfangs-, Zwischen- und Endpunkte ansehen …
<defs>
	<marker id="markerAnfang" markerWidth="6" markerHeight="6" refX="3" refY="3">
		<rect x=".5" y=".5" width="5" height="5" />
	</marker>
	<marker id="markerKreis" markerWidth="6" markerHeight="6" refX="3" refY="3">
		<circle cx="3" cy="3" r="2.5" />
	</marker>
	<marker id="markerPfeil" markerWidth="130" markerHeight="16" refX="3" refY="3">
		<path d="M1,.5 v5 l6,-3z " />
		<text x="15" y="12" stroke="none" fill="#c82f04">Ziel</text>
	</marker>
</defs>
Nachdem die Markierungen definiert sind, können sie beliebig referenziert werden.

Diese Referenzierung kann sowohl als XML-Attribut als auch im CSS erfolgen:
marker,
marker path {
	fill: #dfac20;
	stroke: #000;
	stroke-width: 1;
}

.poly {
	fill: none;
	stroke: #337599;
	stroke-width: 2;
	marker: url(#markerAnfang);
	marker-mid: url(#markerKreis);
	marker-end: url(#markerPfeil);
}

Ist Ihnen oben aufgefallen, dass die Markierungen keinerlei Farbattribute hatten? Die im CSS festgelegten Deklarationen für marker werden auf die Kindelemente vererbt.

Die Pfade mit der Klasse poly erhalten nun an Start-, Zwischen- und Endpunkten Markierungen.

Beachten Sie: Bei Polygonen und Pfaden, die geschlossen wurden, liegt der Endpunkt auf dem Startpunkt. Verwenden Sie entweder marker-end:none oder die gleichen Markierungen.

orient

Im oberen Beispiel entspricht die Lage der End-Markierungen nicht dem Verlauf des Pfades.

Mit dem orient-Attribut können Sie dies anpassen.

automatische Ausrichtung der Endpunkte ansehen …
    <defs>
      <marker id="markerAnfang" markerWidth="7" markerHeight="7" refx="4" refy="4">
        <rect x="1" y="1" width="5" height="5" />
      </marker>
 
      <marker id="markerPfeil" markerWidth="130" markerHeight="13" refx="2" refy="6">
        <path d="M2,2 v8 l8,-4z "/>
      </marker>
	  
      <marker id="markerPfeilAuto" markerWidth="130" markerHeight="13" refx="2" refy="6" orient="auto">
        <path d="M2,2 v8 l8,-4z "/>
      </marker>
    </defs>
 
    <path d="M20,10 v100 h100 m50,0 c100,-140 150,140 250,0 c100,-140 150,140 250,0"
          style="marker: url(#markerPfeil); marker-start: url(#markerAnfang);"
           />
 
    <path d="M20,150 v100 h100 m50,0 c100,-140 150,140 250,0 c100,-140 150,140 250,0"
          style="marker: url(#markerPfeilAuto); marker-start: url(#markerAnfang);"
          />

Der untere Pfad hat automatisch ausgerichtete Markierungen. Die Markierungen für Zwischen und Endpunkte werden mit dem marker-Attribut gesetzt, das dann für den Startpunkt mit marker-start wieder überschrieben wird.

Markierungen stylen

Styling markers is a major PITA tho.
(comment by) Krzysztof Kowalczyk: How To Create SVG Arrowheads and Polymarkers — The marker Element[1]

Die vorherigen Beispiele legen Markierungen an, in denen neben der Form auch die Füllungen und Randlinien bereits fest „verdrahtet“ sind. Es wäre wünschenswert, Markierungen so anlegen zu können, dass bei verschiedenfarbigen Poly-Linien die aktuell verwendeten Farben der Pfade automatisch für die Markierungen übernommen werden. Bisher war so etwas nicht möglich.

In SVG2 sollte es ein neues marker-knockout-Attribut geben, mit der eine Form als Markierung aus dem Strich des Pfades herausgeschnitten werden sollte. Auf der w3.org-Seite finden Sie eine Grafik mit Beispielen.[2] Dies wurde aber von den Browserherstellern nicht implementiert und ist mittlerweile wieder aus den Entwürfen verschwunden.

Im Netz gibt es ein marker-Template, in dem die Werte mit JavaScript ausgetauscht werden. [3]

Mittlerweile gibt es aber Ansätze, dies mit CSS allein zu lösen:


Die vermeintliche Alternative: color

Das color-Attribut legt - anders als bei CSS-color - einen indirekten Wert fest, der nicht gerendert wird. Er dient aber als Grundlage für currentColor, dass dann in Kind-Elementen aufgerufen werden kann.

implizite Farbgebung mit color ansehen …
<marker id="arrow" markerWidth="13" markerHeight="13" refX="6" refY="6" orient="auto">
	<path d="M0,2 l8,4 l-8,4" fill="none" stroke="#999" stroke-width="1"/>
</marker>
<marker id="dot" markerWidth="3" markerHeight="10" refX="1" refY="3" orient="auto">
	<path d="M0,-3 v9" fill="none" stroke="currentColor" stroke-width="3"/>
</marker>
.axis {
  color: #999;
  stroke: currentColor;
  stroke-width: 3;
  marker-mid: url(#dot);
  marker-end: url(#arrow);
}
#axes {
  color: yellow;
}

svg {
  color: red;
  width: 96%;
  max-height: 550px;
  border: thin dotted #337599;
  }

Das Beispiel besteht aus zwei Achsen. Per CSS erhalten sie zwei Markierungen:

  1. die Endmarkierung #arrow hat eine feste Farbgebung mit stroke="#999"
  2. die Markierung #dot hat als Farbwert currentColor.

Im Dokument wird 3x der Wert für color gesetzt:

  • .axis {color: #999; stroke: currentColor;} wirkt auf den stroke der Achse, wird aber für die Markierung ignoriert, weil diese keine Kind-Elemente sind.
  • #axes {color: yellow;} wäre ein Elternelement, wird aber auch ignoriert (würde aber auf den stroke der Achse wirken)
  • erst svg {color: red;} führt zum Ziel, ist für eine implizite Übernahme mehrerer, aktueller Farben aber nicht geeignet.

Fazit: Die color-Eigenschaft bringt hier nichts.

context-fill und context-stroke

In der Spec gibt es nun mit zwei neuen Werten einen neuen Ansatz:

  • context-fill: verwendet den Farbwert der Füllung eines context-Elements
  • context-stroke: verwendet den Farbwert der Randlinie eines context-Elements[4]
implizite Farbgebung mit context-fill und context-stroke ansehen …
<defs>
	<marker id="datamarker" markerWidth="5" markerHeight="5" refX="2" refY="2">
		<circle cx="2" cy="2" r="1.5" stroke-width=".5" fill="context-fill" stroke="context-stroke" />
	</marker>
</defs>

<polyline id="dataline-1" class="dataline" style="stroke:#c82f04; fill:pink;" points="..." />              

<polyline id="dataline-2" class="dataline" style="stroke:#337599; fill:skyblue;" points="..." />              

<polyline id="dataline-3" class="dataline" style="stroke:#dfac20; fill:hsl(43 76% 90%);" points="..." />

Das Beispiel enthält drei polyline-Elemente, die jeweils eine Farbzuweisung für fill und stroke haben. Per CSS wird die Füllfarbe aber mit fill-opacity ausgeblendet, sodass nur die Linien sichtbar sind. Dazu wird mit marker eine Markierung #datamarker referenziert. Diese erhält nun die impliziten Farbwerte fill="context-fill" stroke="context-stroke" und übernimmt so die Festlegungen der Polyline.

Beachten Sie: Dieses Beispiel funktioniert derzeit (Stand: November 2024) im Chrome, Edge, Opera und Firefox!
Safari und ältere Browser rendern die Markierungen in schwarz!

Fazit

Mit dem marker-Element können Sie Markierungen erzeugen, die auf zusätzliche SVG-Elemente verzichten und so Ihr SVG-Markup übersichtlich halten.

Da die Markierungen aber wie die Füllung und Randlinie Teil (nur) eines Grafikobjekts ist, kann man keine einzelnen Markierungen anklicken.[5] Markierungen sind also reine Dekoration; für Interaktivität benötigen Sie andere Elemente.[6]


Referenz

  1. (comment by) Krzysztof Kowalczyk:How To Create SVG Arrowheads and Polymarkers — The marker Element
  2. W3C: MarkerKnockoutLeftProperty
  3. SO: clone colored svg markers + rgbToHex converter
  4. W3C: Specifying paint
  5. Markers cannot be interacted with. Events such as click or mouseover, for example, are not dispatched to a ‘marker’ or its children when the mouse is clicked or moved over a rendered marker. (W3C: Rendering Markers (SVG 2)
    Event attributes and event listeners attached to the contents of a 'marker' element are not processed; only the rendering aspects of 'marker' elements are processed. (W3C: The Marker Element (SVG 1)
  6. Line Markers with SVG (SVGBasics)