mark: SVG - stroke von Umrandungen

Guten Tag,

ein Problem/Verhalten, das mich bei der Verwendung von SVGs stört ist, wenn ich ein Icon habe mit einer umrandenden Linie und diese Umrandung passgenau zum viewport ist (width und height ist nicht gesetzt), so wird diese Linie bei einer Ändernung der stroke-width beschnitten.

Alles, was den Viewport verlässt wird beschnitten.

Ich habe daraufhin gesehen, dass die Browser Firefox & Chrome intern den style "svg:not(:root) { overflow: hidden; }" verwenden. Wenn ich das in meinem stylesheet überschreibe mit overflow: visible; bekomme ich im Firefox das gewünschte Verhalten: der stroke ist außerhalb des viewports sichtbar.

Die einzige Methode die bisher wirklich funktioniert ist, dass ich den viewport eben größer mache. Dann ist der umrandende Stroke sichtbar, aber ich verliere den Vorteil, dass das Icon passgenau ist, weil ja sein viewport nicht mehr bündig ist und ich muss schon beim Erstellen des Icons diesen Stroke berücksichtigen, was sehr frickelig werden kann, wenn ich ein und das selbe Icon in unterschiedlichen Größen darstellen möchte.

Gibt es hierfür eine bessere Lösung?

lg mark

akzeptierte Antworten

  1. Servus!

    Guten Tag,

    ein Problem/Verhalten, das mich bei der Verwendung von SVGs stört ist, wenn ich ein Icon habe mit einer umrandenden Linie und diese Umrandung passgenau zum viewport ist (width und height ist nicht gesetzt), so wird diese Linie bei einer Ändernung der stroke-width beschnitten.

    Alles, was den Viewport verlässt wird beschnitten.

    Lege einfach Werte für x und y mit der halben Konturstärke fest. (Bei stroke-width="2" also x="1", da die Kontur mittig auf dem Rand verläuft.)

    Siehe auch : SVG/Farben/Kontur

    Ich habe daraufhin gesehen, dass die Browser Firefox & Chrome intern den style "svg:not(:root) { overflow: hidden; }" verwenden. [...]

    Die einzige Methode die bisher wirklich funktioniert ist, dass ich den viewport eben größer mache. Dann ist der umrandende Stroke sichtbar, aber ich verliere den Vorteil, dass das Icon passgenau ist, weil ja sein viewport nicht mehr bündig ist und ich muss schon beim Erstellen des Icons diesen Stroke berücksichtigen, was sehr frickelig werden kann, wenn ich ein und das selbe Icon in unterschiedlichen Größen darstellen möchte.

    Geht m.E. nach nicht anders.

    Gibt es hierfür eine bessere Lösung?

    Deine Lösungen sind m.E. eher Umwege, die einfachste Lösung siehe oben.

    lg mark

    Herzliche Grüße

    Matthias Scharwies

    --
    Es gibt viel zu tun - packen wir's an: ToDo-Liste gewünschte Seiten
    1. wenn ich ein Icon habe mit einer umrandenden Linie und diese Umrandung passgenau zum viewport ist (width und height ist nicht gesetzt), so wird diese Linie bei einer Ändernung der stroke-width beschnitten.

      Alles, was den Viewport verlässt wird beschnitten.

      In SVG 2 kannst du dafür stroke-alignment:inside verwenden.

      Gruß Jonathan

    2. @@Matthias Scharwies

      Lege einfach Werte für x und y mit der halben Konturstärke fest. (Bei stroke-width="2" also x="1", da die Kontur mittig auf dem Rand verläuft.)

      So einfach ist das nicht. Dadurch rückt der Rahmen nach innen, näher an das Icon. Je nach Rahmendicke macht sich das mehr oder weniger stark störend bemerkbar.

      Sinnvoller dürfte sein, die ViewBox um die halbe Konturstärke zu vergrößern.

      LLAP 🖖

      --
      “The best way to help people learn: answer their coding question an hour later, they’ll have likely figured it out by then.” —Todd Motto
      Selfcode: sh:) fo:} ch:? rl:) br:> n4:& va:| de:> zu:} fl:{ ss:| ls:# js:|
  2. @@mark

    Die einzige Methode die bisher wirklich funktioniert ist, dass ich den viewport eben größer mache.

    Du meinst viewBox.

    Dann ist der umrandende Stroke sichtbar, aber ich verliere den Vorteil, dass das Icon passgenau ist, weil ja sein viewport nicht mehr bündig ist

    ?? Wenn du die ViewBox nach allen Seiten um die halbe Strichstärke der Rahmenlinie vergrößerst, sollte es genau passen.

    und ich muss schon beim Erstellen des Icons diesen Stroke berücksichtigen, was sehr frickelig werden kann, wenn ich ein und das selbe Icon in unterschiedlichen Größen darstellen möchte.

    Was wäre daran frickelig?

    LLAP 🖖

    --
    “The best way to help people learn: answer their coding question an hour later, they’ll have likely figured it out by then.” —Todd Motto
    Selfcode: sh:) fo:} ch:? rl:) br:> n4:& va:| de:> zu:} fl:{ ss:| ls:# js:|
    1. Hallo Gunnar,

      Du meinst viewBox.

      Ja, es müsste natürlich viewBox heißen.

      Dann ist der umrandende Stroke sichtbar, aber ich verliere den Vorteil, dass das Icon passgenau ist, weil ja sein viewport nicht mehr bündig ist

      ?? Wenn du die ViewBox nach allen Seiten um die halbe Strichstärke der Rahmenlinie vergrößerst, sollte es genau passen.

      Das geht eben nur, wenn die stroke-width nicht variabel ist. Wenn ich ein icon mit unterschiedlicher stroke-width darstellen möchte ist das ein Problem.

      Dies ist beispielsweise dann der Fall, wenn dieses Icon in Unterschiedlichen Größen dargestellt werden soll. Bei kleinen Abmessungen habe ich dann wegen des Antialiasing (Stichwort: Subpixel-Rendering) das Problem dass die Umrandung kaum noch sichtbar ist und ich deshalb die stroke-width verändern muss. Mache ich das aber, schneidet mich die viewBox ab.

      Auch müsste ich die stroke-width, schon im voraus kennen, was nicht der Fall ist, da ich das Icon erst per CSS im Browser auf seine eigentliche Größe bringe.

      Was wäre daran frickelig?

      Mein Workflow, sieht folgendermaßen aus:

      • a) erstelle das Icon
      • b) optimiere das Icon
      • c) binde es in die Webseite ein
      • d) setze/korrigiere width, height, stroke-width per CSS
      • e) sehe, dass die viewBox nicht ausreichend groß ist und das Icon abgeschnitten wird
      • f) korrigiere die viewBox im SVG entsprechend
      • g) kontrollere, ob alles passt und komme drauf, dass mein Icon nun zu klein ist, muss also nochmal width und height entsprechend korrigieren.

      Das ist frickelig. Schritt e, f und g wären überflüssig, wenn der Stroke außerhalb der viewBox sichtbar wäre. Aber es gibt ja gute Gründe, warum die viewBox beschneidet.

      Eine Alternative wäre natürlich, dass ich die viewBox überdimensioniert setze, dass es mit ziemlicher Sicherheit keinen overflow gibt.

      lg

      1. @@mark

        Das geht eben nur, wenn die stroke-width nicht variabel ist. Wenn ich ein icon mit unterschiedlicher stroke-width darstellen möchte ist das ein Problem.

        Dies ist beispielsweise dann der Fall, wenn dieses Icon in Unterschiedlichen Größen dargestellt werden soll. Bei kleinen Abmessungen habe ich dann wegen des Antialiasing (Stichwort: Subpixel-Rendering) das Problem dass die Umrandung kaum noch sichtbar ist und ich deshalb die stroke-width verändern muss.

        Verstehe. Du suchst vector-effect: non-scaling-stroke. Per CSS oder als Attribut.

        LLAP 🖖

        --
        “The best way to help people learn: answer their coding question an hour later, they’ll have likely figured it out by then.” —Todd Motto
        Selfcode: sh:) fo:} ch:? rl:) br:> n4:& va:| de:> zu:} fl:{ ss:| ls:# js:|
        1. Verstehe. Du suchstvector-effect: non-scaling-stroke

          Ich bin mir ziemlich sicher, dass es damit nicht funktioniert.

          Ich habe nun alle Varianten getestet. Matthias Scharwies Methode ist bislang die einzige, die zufriedenstellend funktioniert.

          Eine kleine Anmerkung vielleicht zu Matthias Scharwies Lösungsvorschlag: Statt x/y kann und muss man manchmal sogar transform: translate(X, Y) verwenden.

          Ich habe ein kleines Testdokument zusammengebastelt, damit man wirklich genau versteht, was ich meine und vielleicht ist es dem einen oder anderen hilfreich.

          Ich bin alle drei Lösungsvorschläge durchgegangen. stroke-alignment:inside wird noch nicht unterstützt, sonst wären es 4.

            1. svg:not(:root) {overflow: visible;}
            1. viewBox und x/y anpassen
            1. vector-effect="non-scaling-stroke"

          https://codepen.io/anon/pen/YWgRNN

          <!DOCTYPE html>
          <html lang="de">
          <head>
              <meta charset="UTF-8">
              <title>SVG Bounding Box Test</title>
              
              <style>
                  .svg-samples{
                      display: none;
                  }
                  
                  section{
                      margin-bottom: 5em;
                      padding-bottom: 4em;
                      border-bottom: 3px dashed lightgrey; 
                  }
                  
                  .testing-area{
                      width: 100%;
                      overflow: hidden;
                  }
                  
                  
                  .testing-area svg{
                      display: inline-block;
                      vertical-align: middle;
                      width: 12em;
                      height: 12em;
                      fill: none;
                      margin: 1em;
                  }
                  
                  .testing-area svg.small{
                      width: 3em;
                      height: 3em;
                  }
                  
                  
                  /*
                   * 
                   *  Stroke-width defaults
                   * 
                   * */
                  
                  .rect{
                      stroke: deeppink;
                      stroke-width: 4;
                  }
                  
                  .circle-l{
                      stroke: lightblue;
                      stroke-width: 50;
                  }
                  
                  .circle-s{
                      stroke: lime;
                      stroke-width: .04;
                  }
                  
                  .octagon{
                      stroke: black;
                      stroke-width: .5;
                  }
                  
                  
                  /*
                   * 
                   *  Stroke-width changes
                   * 
                   * */
                  
                  .attempt-1 .rect,
                  .attempt-2 .rect{
                      stroke-width: 44;
                  }
                  
                  .attempt-1 .circle-l,
                  .attempt-2 .circle-l{
                      stroke-width: 400;
                  }
                  
                  .attempt-1 .circle-s,
                  .attempt-2 .circle-s{
                      stroke-width: .3;
                  }
                  
                  .attempt-1 .octagon,
                  .attempt-2 .octagon{
                      stroke-width: 8;
                  }
                  
          
                  .attempt-3 .rect{
                      stroke-width: 1;
                  }
                  
                  .attempt-3 .circle-l{
                      stroke-width: 1;
                  }
                  
          
                  .attempt-3 .circle-s{
                      stroke-width: 1;
                  }
          
                  .attempt-3 .octagon{
                      stroke-width: 1;
                  }
                  
                  
                  /*
                   * 
                   * 
                   *     LÖSUNGSANSATZ 1 svg:not(:root){ overflow: visible; }
                   * 
                   * */
                  
                  .attempt-1 svg:not(:root){
                      overflow: visible;
                  }
                  
                  
              </style>
          </head>
          <body>
          
              <h1>SVG Bouding Box Test</h1>
              
              <svg class="svg-samples">
                  <symbol id="rectangle" preserveAspectRatio="xMidYMid meet" viewBox="0 0 800 800">
                      <rect width="800" height="800" />
                  </symbol>
                  
                  <symbol id="circle-large" preserveAspectRatio="xMidYMid meet" viewBox="0 0 8000 8000">
                      <circle cx="4000" cy="4000" r="4000" />
                  </symbol>
                  
                  <symbol   x="0"  y="0"  id="circle-small" preserveAspectRatio="xMidYMid meet" viewBox="0 0 10 10">
                      <circle cx="5" cy="5" r="5" />
                  </symbol>
                  
                  <symbol id="octagon" preserveAspectRatio="xMidYMid meet" viewBox="0 0 177.9 177.9">
                        <path d="M.2 52.2l52-52h73.4l52 52v73.4l-52 52H52.2l-52-52z" />
                  </symbol>
                  
                  
                  <!--    /*
                           * 
                           * 
                           *     LÖSUNGSANSATZ 2 angepasste viewBox
                           * 
                           * */  
                  -->
                  
                  
                  <!-- +44 -->
                  <symbol id="rectangle-mod" preserveAspectRatio="xMidYMid meet" viewBox="0 0 844 844">
                      <rect x="22" y="22" width="800" height="800" />
                  </symbol>
                  
                  <!-- +400 -->
                  <symbol id="circle-large-mod" preserveAspectRatio="xMidYMid meet" viewBox="0 0 8400 8400">
                      <circle cx="4200" cy="4200" r="4000" />
                  </symbol>
                  
                  <!-- +.3 -->
                  <symbol id="circle-small-mod" preserveAspectRatio="xMidYMid meet" viewBox="0 0 10.3 10.3">
                      <circle cx="5.15" cy="5.15" r="5" />
                  </symbol>
                  
                  <!-- +8 -->
                  <symbol id="octagon-mod" preserveAspectRatio="xMidYMid meet" viewBox="0 0 185.9 185.9">
                        <path transform="translate(4, 4)" d="M.2 52.2l52-52h73.4l52 52v73.4l-52 52H52.2l-52-52z" />
                  </symbol>
                  
                  
                  <!--    /*
                           * 
                           * 
                           *     LÖSUNGSANSATZ 3 non-scaling-stroke
                           * 
                           * */  
                  -->
                  
                  <symbol id="rectangle-non-scaling-stroke" preserveAspectRatio="xMidYMid meet" viewBox="0 0 800 800">
                      <rect vector-effect="non-scaling-stroke" stroke-width="4" width="800" height="800" />
                  </symbol>
                  
                  <symbol id="circle-large-non-scaling-stroke" preserveAspectRatio="xMidYMid meet" viewBox="0 0 8000 8000">
                      <circle vector-effect="non-scaling-stroke" cx="4000" cy="4000" r="4000" />
                  </symbol>
                  
                  <symbol id="circle-small-non-scaling-stroke" preserveAspectRatio="xMidYMid meet" viewBox="0 0 10 10">
                      <circle vector-effect="non-scaling-stroke" cx="5" cy="5" r="5" />
                  </symbol>
                  
                  <symbol id="octagon-non-scaling-stroke" preserveAspectRatio="xMidYMid meet" viewBox="0 0 177.9 177.9">
                        <path vector-effect="non-scaling-stroke" d="M.2 52.2l52-52h73.4l52 52v73.4l-52 52H52.2l-52-52z" />
                  </symbol>
                          
              </svg>
                         
              
              <section class="problem">
                  <h2>Das Problem:</h2>
                  
                  <div>
                     Idealerweise wären große und kleine Icons in der Farbintensität identisch. Dafür ist jedoch eine Änderung der
                     stroke-width nötig, sonst ist die Umrandung ggf. nicht mehr erkennbar. Siehe nachfolgende Darstellung: zuerst sind die Icons
                     groß abgebildet, dann, fast nicht sichtbar klein. Bei einem Rechteck ist das Problem nicht sofort ersichtlich,
                     da der Stroke gleichmäßig beschnitten wird.
                      
                      <ul>
                          <li>Die Stroke-width ist abhängig von der viewBox.</li>
                          <li>Die ViewBox beschneidet den Stroke.</li> 
                      </ul>
                  </div>
                  
                  <div class="testing-area">
                      <svg class="rect">
                          <use xlink:href="#rectangle" />
                      </svg>
                      
                      <svg class="circle-l">
                          <use xlink:href="#circle-large" />
                      </svg>
                      
                      <svg class="circle-s">
                          <use xlink:href="#circle-small" />
                      </svg>
                      
                      <svg class="octagon">
                          <use xlink:href="#octagon" />
                      </svg>
                      
                      <svg class="rect small">
                          <use xlink:href="#rectangle" />
                      </svg>
                      
                      <svg class="circle-l small">
                          <use xlink:href="#circle-large" />
                      </svg>
                      
                      <svg class="circle-s small">
                          <use xlink:href="#circle-small" />
                      </svg>
                      
                      <svg class="octagon small">
                          <use xlink:href="#octagon" />
                      </svg>
                  </div>
              </section>    
              
              
              
              <section class="attempt attempt-1">
                  <h2>Lösungsansatz 1</h2>
                  
                  <p>
                      svg:not(:root) {overflow: visible;}<br />
                      Nachteil: funktioniert nur im Firefox
                  </p>
                  
                  <div class="testing-area"> 
                      <svg class="rect small">
                          <use xlink:href="#rectangle" />
                      </svg>
                      
                      <svg class="circle-l small">
                          <use xlink:href="#circle-large" />
                      </svg>
                      
                      <svg class="circle-s small">
                          <use xlink:href="#circle-small" />
                      </svg>
                      
                      <svg class="octagon small">
                          <use xlink:href="#octagon" />
                      </svg>
                  </div>
              </section>
              
              <section class="attempt attempt-2">
                  <h2>Lösungsansatz 2</h2>
                  
                  <p>
                      Anpassung der viewBox<br />
                      Vorteil: Funktioniert in allen Browsern.<br />
                      Nachteil: relativ aufwendig.<br />
                      Bemerkung: Verschiebung der X/Y-Koordinate kann, bzw. muss in manchen Fällen mit transform: translate(X, Y) erfolgen.
                  </p>
                  
                  <div class="testing-area">
                      <svg class="rect small">
                          <use xlink:href="#rectangle-mod" />
                      </svg>
                      
                      <svg class="circle-l small">
                          <use xlink:href="#circle-large-mod" />
                      </svg>
                      
                      <svg class="circle-s small">
                          <use xlink:href="#circle-small-mod" />
                      </svg>
                      
                      <svg class="octagon small">
                          <use xlink:href="#octagon-mod" />
                      </svg>
                  </div>
              </section>                                   
              
              <section class="attempt attempt-3">
                  <h2>Lösungsansatz 3</h2>
                  
                  <p>
                      vector-effect="non-scaling-stroke"<br />
                      Funktioniert nicht: beschneidet die stroke-width.<br />
                      Funktioniert nicht im IE11. Edge konnte ich nicht testen.
                  </p>
                  
                  <div class="testing-area">
                      <svg class="rect small">
                          <use xlink:href="#rectangle-non-scaling-stroke" />
                      </svg>
                      
                      <svg class="circle-l small">
                          <use xlink:href="#circle-large-non-scaling-stroke" />
                      </svg>
                      
                      <svg class="circle-s small">
                          <use xlink:href="#circle-small-non-scaling-stroke" />
                      </svg>
                      
                      <svg class="octagon small">
                          <use xlink:href="#octagon-non-scaling-stroke" />
                      </svg>
                  </div>
              </section>                                                                                                                                                                                                                                                                             
          </body>
          </html>
          
          1. @@mark

            Verstehe. Du suchst vector-effect: non-scaling-stroke Ich bin mir ziemlich sicher, dass es damit nicht funktioniert.

            Ich bin mir ziemlich sicher, dass es damit vor ein paar Tagen bei mir funktioniert hat.

            Gerade nochmal positiv getestet in Firefox und Safari.

            LLAP 🖖

            --
            “The best way to help people learn: answer their coding question an hour later, they’ll have likely figured it out by then.” —Todd Motto
            Selfcode: sh:) fo:} ch:? rl:) br:> n4:& va:| de:> zu:} fl:{ ss:| ls:# js:|
            1. Ich bin mir ziemlich sicher, dass es damit vor ein paar Tagen bei mir funktioniert hat.

              Gerade nochmal positiv getestet in Firefox und Safari.

              Ich habs gerade eben negativ getestet. Siehe meinen verlinkten Test: https://codepen.io/anon/pen/YWgRNN

              Wenn du magst kannst du mir ja deinen Test zeigen, oder meinen korrigieren. Ich kriegs nicht zum laufen.

              Auch mag der IE11 (Edge hab' ich nicht getestet) kein vector-effect: non-scaling-stroke, was die Latte dieses Attribut zu verwenden nochmal höher hängt.

              1. @@mark

                Ich habs gerade eben negativ getestet.

                Ah ja, die halbe Strichstärke des Rahmens liegt dann ja außerhalb der ViewBox (wenn der Pfad genau auf deren Rand liegt).

                Die ViewBox größer zu machen dürfte nicht gehen, da deren Ausmaße ja das mitskalierende Koordinatensystem angeben, die Strichstärke des Rahmens bei vector-effect: non-scaling-stroke aber gerade nicht mitskaliert.

                Abhilfe: padding mit halber Strichstärke des Rahmens im Zusammenhang mit overflow: visible. →Pen

                Auch mag der IE11 (Edge hab' ich nicht getestet) kein vector-effect: non-scaling-stroke, was die Latte dieses Attribut zu verwenden nochmal höher hängt.

                Vielleicht für alte Browser (und für ganz alte sowieso) eine Rastergrafik als Fallback verwenden?
                @supports(vector-effect: non-scaling-stroke) {}

                LLAP 🖖

                --
                “The best way to help people learn: answer their coding question an hour later, they’ll have likely figured it out by then.” —Todd Motto
                Selfcode: sh:) fo:} ch:? rl:) br:> n4:& va:| de:> zu:} fl:{ ss:| ls:# js:|