Können Sie steuern, wie die Strichbreite eines SVG gezeichnet wird?

204

Derzeit wird eine browserbasierte SVG-Anwendung erstellt. Innerhalb dieser App können verschiedene Formen vom Benutzer gestaltet und positioniert werden, einschließlich Rechtecke.

Wenn ich a stroke-widthauf ein SVG- rectElement von beispielsweise anwende 1px, wird der Strich rectvon verschiedenen Browsern auf unterschiedliche Weise auf den Versatz und den Einschub des Strichs angewendet . Dies erweist sich als problematisch, insbesondere wenn ich versuche, die äußere Breite und die visuelle Position eines Rechtecks ​​zu berechnen und neben anderen Elementen zu positionieren.

Beispielsweise:

  • Firefox fügt einen 1-Pixel-Einschub (unten und links) und einen 1-Pixel-Versatz (oben und rechts) hinzu.
  • Chrome fügt einen 1-Pixel-Einschub (oben und links) und einen 1-Pixel-Versatz (unten und rechts) hinzu.

Meine bisher einzige Lösung wäre, die tatsächlichen Ränder selbst zu zeichnen (wahrscheinlich mit dem pathWerkzeug) und die Ränder hinter dem gestrichenen Element zu positionieren. Aber diese Lösung ist eine unangenehme Problemumgehung, und ich würde es vorziehen, diesen Weg nicht zu gehen, wenn dies möglich ist.

Meine Frage ist also, können Sie steuern, wie eine SVG stroke-widthauf Elementen gezeichnet wird?

Steve
quelle
Es gibt Filter-Hacks, mit denen Sie dies erreichen können - aber es ist keine großartige Lösung
Michael Mullany
2
Es gibt den paint-orderParameter, in dem Sie angeben können, dass die Füllung über dem Strich gerendert werden soll, damit Sie die "äußere Ausrichtung" erhalten, siehe jsfiddle.net/hne0kyLg/1
Ivan Kuckir
Es wurde eine Möglichkeit gefunden, dies mithilfe der CSS-Attribute 'Outline-' zu tun: codepen.io/badcat/pen/YVzmYY . Ich bin mir nicht sicher, welche Unterstützung dies für alle Browser bietet, könnte aber nützlich sein.
bplmp

Antworten:

375

Nein, Sie können nicht angeben, ob der Strich innerhalb oder außerhalb eines Elements gezeichnet wird. Ich habe der SVG-Arbeitsgruppe 2003 einen Vorschlag für diese Funktionalität unterbreitet , der jedoch keine Unterstützung (oder Diskussion) erhielt.

SVG schlug ein Beispiel für die Schlaganfallposition von phrogz.net/SVG/stroke-location.svg vor

Wie ich im Vorschlag festgestellt habe,

  • Sie können das gleiche visuelle Ergebnis wie "innen" erzielen, indem Sie Ihre Strichbreite verdoppeln und dann einen Beschneidungspfad verwenden, um das Objekt an sich selbst zu befestigen
  • Sie können das gleiche visuelle Ergebnis wie "außen" erzielen, indem Sie die Strichbreite verdoppeln und dann eine Kopie des Objekts ohne Strich über sich selbst legen.

Bearbeiten : Diese Antwort könnte in Zukunft falsch sein. Es sollte möglich sein, diese Ergebnisse mit SVG-Vektoreffekten zu erzielen , indem sie veStrokePathmit veIntersect(für 'innen') oder mit veExclude(für 'außen ') kombiniert werden . Vector Effects ist jedoch immer noch ein funktionierendes Entwurfsmodul ohne Implementierungen, die ich noch finden kann.

Bearbeiten 2 : Die SVG 2-Entwurfsspezifikation enthält eine stroke-alignmentEigenschaft (mit Mitte | innerhalb | außerhalb möglicher Werte). Diese Eigenschaft kann es schließlich in UAs schaffen.

Edit 3 : Amüsanterweise und enttäuschend hat sich die SVG-Arbeitsgruppe stroke-alignmentaus SVG 2 entfernt . Sie können einige der nach der Prosa beschriebenen Bedenken hier sehen .

Phrogz
quelle
2
Dachte, das könnte der Fall sein. Um dies zu lösen, habe ich eine Wrapper-Funktion für getBBox () namens getStrokedBBox () geschrieben. Dieser Wrapper gibt die BBox zurück, je nachdem, wie der Browser Striche auf den Einschub und den Versatz einer Form anwendet. Es ist nicht perfekt (muss ständig mit den neuesten Browserversionen verglichen werden), bietet jedoch vorerst genau die Form einer Außenbreite.
Steve
6
@Phrogz vielleicht werden wir es sehen, bevor 10 Jahre vergangen sind. svgwg.org/svg2-draft/painting.html#SpecifyingStrokePaint Annotation
Frenchone
1
Es ist endlich da! Zum einen habe ich wirklich darauf gedrängt, dass diese Immobilie ankommt.
mnsth
Ich habe eine svg-Kontur Skript für Kontur jeder SVGGeometryElement Tracing , die als Behelfslösung für Schlaganfall-Ausrichtung verwendet werden kann, für alle Interessierten Sie eine Beschreibung finden Sie hier
maioman
2
Gibt es eine Seite, auf der ich für den Vorschlag stimmen kann? Vielen Dank. Scheint lächerlich, es wird nicht unterstützt.
Mahish
57

UPDATE: Das stroke-alignmentAttribut wurde am 1. April 2015 auf eine völlig neue Spezifikation namens SVG Strokes verschoben .

Ab dem Entwurf des SVG 2.0-Editors vom 26. Februar 2015 (und möglicherweise seit dem 13. Februar )Das stroke-alignmentAnwesen ist vorhandenmit den Werten inner, center (default) und outer.

Es scheint genauso zu funktionieren wie die stroke-locationvon @Phrogz vorgeschlagene Eigenschaft und der spätere stroke-positionVorschlag . Diese Eigenschaft ist seit mindestens 2011 geplant, aber abgesehen von einer Anmerkung, die besagte

SVG 2 muss eine Möglichkeit zur Angabe der Hubposition enthalten

Es wurde in der Spezifikation nie detailliert beschrieben, da es verschoben wurde - bis jetzt scheint es.

Kein Browser unterstützt diese Eigenschaft oder, soweit ich weiß, noch eine der neuen SVG 2-Funktionen, aber hoffentlich werden sie bald verfügbar sein, wenn die Spezifikation ausgereift ist. Dies war eine Immobilie, auf die ich persönlich gedrängt habe, und ich bin wirklich froh, dass sie endlich in der Spezifikation enthalten ist.

Es scheint einige Probleme zu geben, wie sich die Eigenschaft auf offenen Pfaden und Schleifen verhalten soll. Diese Probleme werden höchstwahrscheinlich die Implementierung in allen Browsern verlängern. Ich werde diese Antwort jedoch mit neuen Informationen aktualisieren, sobald Browser diese Eigenschaft unterstützen.

mnsth
quelle
1
stroke-alignmentwird in SVG Strokes, einem W3C-Arbeitsentwurf, angegeben. In der Zwischenzeit heißt es im Entwurf des SVG 2 W3C-Editors, dass eine Strichpositionierungseigenschaft in der SVG-Spezifikation unter svgwg.org/svg2-draft/painting.html#SpecifyingStrokePaint enthalten sein soll, diese Spezifikation jedoch bereits den W3C-Kandidatenempfehlungsstatus erreicht hat und keine solche Eigenschaft vorliegt Abgesehen von einem Link zum stroke-positionVorschlag ist dies in der Spezifikation nicht der Fall.
Patrick Dark
47

Ich habe einen einfachen Weg gefunden, der einige Einschränkungen aufweist, aber für mich funktioniert hat:

  • Definieren Sie die Form in defs
  • Definieren Sie einen Clip-Pfad, der auf die Form verweist
  • Verwenden Sie es und verdoppeln Sie den Strich mit, wenn die Außenseite abgeschnitten wird

Hier ein Arbeitsbeispiel:

<svg width="240" height="240" viewBox="0 0 1024 1024">
<defs>
	<path id="ld" d="M256,0 L0,512 L384,512 L128,1024 L1024,384 L640,384 L896,0 L256,0 Z"/>
	<clipPath id="clip">
		<use xlink:href="#ld"/>
	</clipPath>
</defs>
<g>
	<use xlink:href="#ld" stroke="#0081C6" stroke-width="160" fill="#00D2B8" clip-path="url(#clip)"/>
</g>
</svg>

Jörg Janke
quelle
2
Die beste Antwort, die ich gefunden habe
Ed Kolosovsky
1
Clevere Lösung. Vielen Dank
Vince Yuan
Dies sollte als Antwort akzeptiert werden. Die Verwendung von <defs> und <use> ist die derzeit eleganteste Lösung.
Nyerguds
Schöne Lösung. Wie würden Sie es umkehren, um einen Außenhub auszuführen?
Lucent
Warum die oberste Ebene <use>? Warum nicht einfach den Pfad und den Clip-Pfad direkt eingeben? Das funktioniert gut : <svg width="240" height="240" viewBox="0 0 1024 1024"> <path id="ld" d="M256,0 L0,512 L384,512 L128,1024 L1024,384 L640,384 L896,0 L256,0 Z" clip-path="url(#clip)" stroke="#0081C6" stroke-width="160" fill="#00d2b8"/> <clipPath id="clip"> <use xlink:href="#ld"/> </clipPath> </svg>. (Ja, dies ist völlig vernünftig und korrekt. SVG wird überall korrekt wiedergegeben. Fürchte keine Rekursion.)
Chris Morgan
30

Sie können CSS verwenden, um die Reihenfolge von Strichen und Füllungen zu gestalten. Das heißt, zuerst streichen und dann füllen, um den gewünschten Effekt zu erzielen.

MDN unter paint-order: https://developer.mozilla.org/en-US/docs/Web/SVG/Attribute/paint-order

CSS-Code:

paint-order: stroke;
Xavier Ho
quelle
Dies ist perfekt! Vielen Dank für das Teilen
BrunoFenzl
1
Die verknüpfte Dokumentation scheint nicht anzugeben, ob der Strich innen, außen oder zentriert ist. Können Sie klarstellen, wie Sie steuern können, wo der Strich gezeichnet wird? Vielen Dank.
Crashalot
2
Der Strich wird wie immer zentriert - aber wenn Sie eine feste Füllung haben, bewirkt das Anpassen der Farbreihenfolge an den ersten Malstrich, dass die innere Hälfte des Strichs übermalt wird, was den gleichen Effekt hat wie das Zeichnen von a halb so dicker Strich draußen.
Chris Morgan
7

Hier ist eine Funktion, die berechnet, wie viele Pixel Sie mithilfe des angegebenen Strichs oben, rechts, unten und links hinzufügen müssen, basierend auf dem Browser:

var getStrokeOffsets = function(stroke){

        var strokeFloor =       Math.floor(stroke / 2),                                                                 // max offset
            strokeCeil =        Math.ceil(stroke / 2);                                                                  // min offset

        if($.browser.mozilla){                                                                                          // Mozilla offsets

            return {
                bottom:     strokeFloor,
                left:       strokeFloor,
                top:        strokeCeil,
                right:      strokeCeil
            };

        }else if($.browser.webkit){                                                                                     // WebKit offsets

            return {
                bottom:     strokeCeil,
                left:       strokeFloor,
                top:        strokeFloor,
                right:      strokeCeil
            };

        }else{                                                                                                          // default offsets

            return {
                bottom:     strokeCeil,
                left:       strokeCeil,
                top:        strokeCeil,
                right:      strokeCeil
            };

        }

    };
Steve
quelle
6

Wie oben erwähnt, müssen Sie entweder einen Versatz zu den Pfadkoordinaten des Strichs neu berechnen oder seine Breite verdoppeln und dann die eine oder andere Seite maskieren, da SVG nicht nur die Strichausrichtung von Illustrator nicht nativ unterstützt, PostScript auch nicht .

Die Spezifikation für Anschläge in Adobe Postscript - Handbuch 2. Ausgabe heißt es : " 4.5.1 Streicheln: Der Schlaganfall Bediener zieht eine Linie von einem gewissen Dicke entlang des Strompfades für jedes geraden oder gebogenes Segment im Weg. Schlaganfall eine Linie zieht , die sich mittig auf das Segment mit parallelen Seiten zum Segment. " (Hervorhebung ihrer)

Der Rest der Spezifikation enthält keine Attribute zum Versetzen der Position der Linie. Wenn Sie mit Illustrator innen oder außen ausrichten können, wird der tatsächliche Pfadversatz neu berechnet (da er immer noch rechnerisch billiger ist als Überdrucken und dann Maskieren). Die Pfadkoordinaten im .ai-Dokument sind Referenzdaten und nicht das, was gerastert oder in ein endgültiges Format exportiert wird.

Da das native Format von Inkscape die Spezifikation SVG ist, kann es keine Funktion bieten, die der Spezifikation fehlt.

user1574945
quelle
4

Hier ist eine Problemumgehung für die innere Begrenzung rect mit symbolund use.

Beispiel : https://jsbin.com/yopemiwame/edit?html,output

SVG :

<svg>
  <symbol id="inner-border-rect">
    <rect class="inner-border" width="100%" height="100%" style="fill:rgb(0,255,255);stroke-width:10;stroke:rgb(0,0,0)">
  </symbol>
  ...
  <use xlink:href="#inner-border-rect" x="?" y="?" width="?" height="?">
</svg>

Hinweis: Stellen Sie sicher , ersetzen die ?inuse mit realen Werten.

Hintergrund : Der Grund , warum dies funktioniert , ist , weil Symbol ein neues Ansichtsfenster stellt durch Austausch symbolmit svgund ein Element im Schatten DOM zu schaffen. Dieses svgdes Schatten-DOM wird dann mit Ihrem aktuellen SVGElement verknüpft . Beachten Sie, dass svgs verschachtelt werden kann und jedes svgein neues Ansichtsfenster erstellt, das alles überlappt, was sich überlappt, einschließlich des überlappenden Rahmens. Für einen viel detaillierteren Überblick über das Geschehen lesen Sie diesen fantastischen Artikel von Sara Soueidan.

F Lekschas
quelle
2

Ich weiß nicht, wie hilfreich das sein wird, aber in meinem Fall habe ich nur einen weiteren Kreis mit nur Rand erstellt und ihn „innerhalb“ der anderen Form platziert.

Der Zeuge
quelle
0

Eine (schmutzige) mögliche Lösung ist die Verwendung von Mustern,

Hier ist ein Beispiel mit einem innen gestrichenen Dreieck:

https://jsfiddle.net/qr3p7php/5/

<style>
#triangle1{
  fill: #0F0;
  fill-opacity: 0.3;
  stroke: #000;
  stroke-opacity: 0.5;
  stroke-width: 20;
}
#triangle2{
  stroke: #f00;
  stroke-opacity: 1;
  stroke-width: 1;
}    
</style>

<svg height="210" width="400" >
    <pattern id="fagl" patternUnits="objectBoundingBox" width="2" height="1" x="-50%">
        <path id="triangle1" d="M150 0 L75 200 L225 200 Z">
    </pattern>    
    <path id="triangle2" d="M150 0 L75 200 L225 200 Z" fill="url(#fagl)"/>
</svg>
max-lt
quelle
0

Die Lösung von Xavier Ho, die Breite des Strichs zu verdoppeln und die Farbreihenfolge zu ändern, ist brillant, funktioniert jedoch nur, wenn die Füllung einfarbig und ohne Transparenz ist.

Ich habe einen anderen Ansatz entwickelt, der komplizierter ist, aber für jede Füllung funktioniert. Es funktioniert auch in Ellipsen oder Pfaden (bei letzteren gibt es einige Eckfälle mit seltsamem Verhalten, zum Beispiel offene Pfade, die sich kreuzen, aber nicht viel).

Der Trick besteht darin, die Form in zwei Ebenen anzuzeigen. Eine ohne Strich (nur Füllung) und eine andere nur mit Strich bei doppelter Breite (transparente Füllung) und durch eine Maske geführt, die die gesamte Form zeigt, aber die ursprüngliche Form ohne Strich verbirgt.

  <svg width="240" height="240" viewBox="0 0 1024 1024">
  <defs>
    <path id="ld" d="M256,0 L0,512 L384,512 L128,1024 L1024,384 L640,384 L896,0 L256,0 Z"/>
    <mask id="mask">
      <use xlink:href="#ld" stroke="#FFFFFF" stroke-width="160" fill="#FFFFFF"/>
      <use xlink:href="#ld" fill="#000000"/>
    </mask>
  </defs>
  <g>
    <use xlink:href="#ld" fill="#00D2B8"/>
    <use xlink:href="#ld" stroke="#0081C6" stroke-width="160" fill="red" mask="url(#mask)"/>
  </g>
  </svg>
Hirunatan
quelle