SVG/Farben/Markierungen
- 20min
- mittel
- • Painting -
wie wird in SVG „gemalt“?
• Gruppierungen (symbol)
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.
Inhaltsverzeichnis
marker - ein wiederverwendbares Symbol
Das marker-Element dient dazu - wie symbol oder pattern - ein Grafikobjekt im Definitionsabschnitt anzulegen, dass dann mehrfach referenziert werden kann.
<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 RandlinienuserSpaceOnUse
: 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.
<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>
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.
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.
<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.
<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:
- die Endmarkierung
#arrow
hat eine feste Farbgebung mitstroke="#999"
- 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]
<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.
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]
- Geometrie mit MathML, SVG und JavaScript
Dreieck, dessen Punkte mit Drag & Drop gezogen werden können.
Referenz
- ↑ (comment by) Krzysztof Kowalczyk:How To Create SVG Arrowheads and Polymarkers — The marker Element
- ↑ W3C: MarkerKnockoutLeftProperty
- ↑ SO: clone colored svg markers + rgbToHex converter
- ↑ W3C: Specifying paint
- ↑ 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) - ↑ Line Markers with SVG (SVGBasics)
Diese Referenzierung kann sowohl als XML-Attribut als auch im CSS erfolgen: