skip to Main Content

Since I’m an expert in eating cookies I thought by myself, why not become en expert in SVG as well – you’ll never know what it might be good for some day…

So what I’ve chosen to achieve is to draw a filled circle with an exclamation mark inside. I’ve read some tutorials and I decided to use only one path object for that. Drawing the circle seems to be quite easy:

<?xml version="1.0" encoding="utf-8"?>
<svg xmlns="http://www.w3.org/2000/svg">
  <defs>
    <symbol id="circle" viewBox="0 0 24 24" aria-hidden="true">

      <path d="M12 0 A12 12 0 1 1 11.99 0Z" stroke="none"/>

    </symbol>
  </defs>
</svg>

<!DOCTYPE html>
<html>
<head>
  <meta charset="UTF-8" />
  <title>SVG Test</title>

  <style>
    .circle {
      width: 1rem;
      height: 1rem;
      fill: red;
    }
  </style>
    
</head>

<body>

  <svg class="circle"><use href="icons.svg#circle"/></svg>

</body>
</html>

As the path shows, it has to be drawn to an endpoint of (11.99|0). This seems a little odd to me, but if I draw it exactly to (12|0) the circle won’t get drawn onto the canvas.
So my first question
is:

Q1) Why can’t I close the circle at the starting point but have to stop some point before to close the path with `z`?

My second question is regarding the viewBox. As far as I know the path itself has no dimensions. So this viewBox starts at (0|0) and ends ad (24|24).

Q2) Is this viewBox width and height totally equivalent to 24px in case we give the SVG these values, or are we dealing with an off by 0.00…001?

And now it gets a little weird, at least for me. I want to crop out the exclamation mark from the middle of the circle. I’ve read somewhere, that to do this, you have to draw the circle in exactly the other direction as you draw the inner path. In my following example I’m drawing the circle clockwise (which btw requires to end the circle at 11.99 while in case drawing the circle counter-clockwise would require to end the path at 12.01). And the rectangle inside gets removed:

<?xml version="1.0" encoding="utf-8"?>
<svg xmlns="http://www.w3.org/2000/svg">
  <defs>
    <symbol id="circle-with-line" viewBox="0 0 24 24" aria-hidden="true">

      <path d="M12 0 A12 12 0 1 1 11.99 0Z M11 6 v8 h3 V6 H11Z" stroke="none"/>

    </symbol>
  </defs>
</svg>

Unfortunately, this rectangle doesn’t get cut out sharply. I’d expected that starting the rectangle at an exact point and drawing lines exactly along the axis should give me a sharp and crisp cut. But that’s not the case, the edges are blurry. So question number three comes here:

Q3) Why is the cut out not sharp lined with the monitor pixels and how to fix this?

As I’ve mentioned above, I’m new to SVG and the edgcases seem not to be covered in the beginners turorials. So please be patient with me.

As always, many thanks for any help

2

Answers


  1. Chosen as BEST ANSWER

    Q3) Why is the cut out not sharp lined with the monitor pixels and how to fix this?

    Well, this mystery got solved. For everybody facing these problems the first time like me, here the answer:

    The edges stay sharp as long as the viewport's dimensions equal the SVG dimensions. In this case:

    <style>
      .circle {
        width: 24px;
        height: 24px;
      }
    </style>
    

    For demonstration I've used px, but I will rather stay with rem and try to match the dimensions assuming 16px standard for 1rem.

    Also, any multiple of the viewPort size should also result in sharp edges, means 48, 72, 96...


  2. Q1: The answer can be found with a simple experiment. Consider the following two circular paths, with the start point at the local coordinate system origin. One has the end point slightly to the right, one slightly below. The first arc is drawn above the start point, the second to the right. Now imagine the distance of the start and end point being reduced to zero. Which direction should the arc be be drawn in? Both variants degrade to the same singularity. Since there is no unique solution, the path definition is in error and thus not drawn.

    .circle {
      fill: lightgrey;
      stroke: black;
    }
    
    .mark {
      fill: none;
      stroke: blue;
    }
    <svg height="100vh" viewBox="0 0 100 60">
      <g transform="translate(25,30)">
        <path class="circle" d="M0 0 A12 12 0 1 1 0.01 0Z" />
        <path class="mark" d="M4 0h-8M0 4v-8" />
      </g>
      <g transform="translate(75,30)">
        <path class="circle" d="M0 0 A12 12 0 1 1 0 0.01Z" />
        <path class="mark" d="M4 0h-8M0 4v-8" />
      </g>
    </svg>

    To resolve this, most grafical editors (if they support the arc command) draw two half-circles:

    d="M0,0A12 12 0 0 1 0,24 12 0 0 1 0,0 z"
    

    …or simply use a grafical primitive element:

    <circle r="12" cx="12" cy="0" />
    

    Q2: Things get as precise as the viewport that renders the grafic can be. The algorithm that describes how to go from the coordinate system described by the viewBox to the coordinate system for the containing viewport is documented here.

    Q3: Things get as precise as the viewport that renders the grafic can be. If while transforming an integer coordinate gets transformed to a pixel value with a fractional part, a renderer that can only display one pixel at a time will resort to interpolating techniques like antialiasing.

    There are limited possibilities to affect how these techniques are used. The presentation attribute shape-rendering will set a hint which way of rendering should be prioritized: crisp edges, smooth curves or rendering speed?

    While these are hints the renderes can interpret in their own way, it is pretty standard that shape-rendering: crispEdges will result in antialiasing (the reason for the "blurry" edges) being deactivated.

    Login or Signup to reply.
Please signup or login to give your own answer.
Back To Top
Search