93

I'm not sure how to approach drawing a hollow circle in SVG.

I would like a ring shape filled with a colour and then have a black outline.

The way I thought about doing it was have 2 circles, one with a smaller radius than the other. The problem is when I fill them, how do I make the smaller circle take the same fill colour as what it sits on?

luketorjussen
  • 3,156
  • 1
  • 21
  • 38

6 Answers6

140

Just use fill="none" and then only the stroke (outline) will be drawn.

<svg xmlns="http://www.w3.org/2000/svg" version="1.1">
   <circle cx="100" cy="50" r="40" stroke="black" stroke-width="2" fill="none" />
</svg> 

Or this if you want two colours:

<svg xmlns="http://www.w3.org/2000/svg" version="1.1">
   <circle cx="100" cy="50" r="40" stroke="black" stroke-width="3" fill="none" />
   <circle cx="100" cy="50" r="39" stroke="red" stroke-width="2" fill="none" />
</svg>
Robert Longson
  • 118,664
  • 26
  • 252
  • 242
  • 10
    The problem with this is it doesn't allow me to keep the black outline as well. I would like a ring shape with a black outline. – luketorjussen Nov 19 '11 at 11:36
  • 2
    @luketorjussen: to me, this is just a black outline. if you want a different fill color, just change the fill attribute – rampion Nov 19 '11 at 12:08
  • 1
    @rampiom: I want to have a ring of a given colour, all with a black outline – luketorjussen Nov 19 '11 at 12:16
  • you can do two strokes one on top of another with a different radius so one sticks out – Robert Longson Nov 19 '11 at 12:44
  • 1
    Thanks, this works for drawing a hollow circle :-) I would like to extend this to drawing any shape hollowed out by a different shape (e.g. A Square with the middle circle hollow) - I have posted another question for this [here](http://stackoverflow.com/questions/8193675/draw-a-hollow-circle-in-svg/8199621#8199621) – luketorjussen Nov 20 '11 at 09:18
  • This is neither a ring nor a donut: the first instance will not scale, the second will not intersect properly with other shapes. This answer should be demoted, the 3 answers above it are correct. – Jean-Rene Bouvier Dec 19 '21 at 16:32
24

MDragon00's answer works, but the inner and outer circles are not perfectly aligned (e.g. centered).

I modified his approach a little, using 4 semi-circle arcs (2 outer and 2 inner in reversed direction) to get the alignment exactly right.

<svg width="100" height="100">
  <path d="M 50 10 A 40 40 0 1 0 50 90 A 40 40 0 1 0 50 10 Z M 50 30 A 20 20 0 1 1 50 70 A 20 20 0 1 1 50 30 Z" fill="#0000dd" stroke="#00aaff" stroke-width="3" />
</svg>

<!--

Using this path definition as d:

M centerX (centerY-outerRadius)
A outerRadius outerRadius 0 1 0 centerX (centerY+outerRadius)
A outerRadius outerRadius 0 1 0 centerX (centerY-outerRadius)
Z
M centerX (centerY-innerRadius)
A innerRadius innerRadius 0 1 1 centerX (centerY+innerRadius)
A innerRadius innerRadius 0 1 1 centerX (centerY-innerRadius)
Z

-->
Philip
  • 2,888
  • 2
  • 24
  • 36
19

Thanks to Chasbeen, I figured out how to make a true ring/donut in SVG. Note that the outer circle actually isn't closed, which is only apparent when you use a stroke. Very useful when you have many concentric rings, especially if they're interactive (say, with CSS hover commands).

For the draw command...

M cx, cy // Move to center of ring
m 0, -outerRadius // Move to top of ring
a outerRadius, outerRadius, 0, 1, 0, 1, 0 // Draw outer arc, but don't close it
Z // default fill-rule:even-odd will help create the empty innards
m 0 outerRadius-innerRadius // Move to top point of inner radius
a innerRadius, innerRadius, 0, 1, 1, -1, 0 // Draw inner arc, but don't close it
Z // Close the inner ring. Actually will still work without, but inner ring will have one unit missing in stroke       

JSFiddle - Contains several rings and CSS to simulate interactivity. Note the downside that there's a single pixel missing at the starting point (at the top), which is only there if you add a stroke on.

Edit: Found this SO answer (and better yet, this answer) which describes how to get the empty innards in general

Lee Taylor
  • 7,761
  • 16
  • 33
  • 49
DragonJawad
  • 1,846
  • 3
  • 20
  • 28
8

You can do this as per the SVG spec by using a path with two components and fill-rule="evenodd". The two components are semi-circular arcs which join to form a circle (in the "d" attribute below, they each end with a 'z'). The area inside the inner circle does not count as part of the shape, hence interactivity is good.

To decode the below a little, the "340 260" is the top middle of the outer circle, the "290 290" is the radius of the outer circle (twice), the "340 840" is the bottom middle of the outer circle, the "340 492" is the top middle of the inner circle, the "58 58" is the radius of the inner circle (twice) and the "340 608" is the bottom middle of the inner circle.

<svg viewBox="0 0 1000 1000" xmlns="http://www.w3.org/2000/svg">
    <path fill-rule="evenodd" d="M340 260A290 290 0 0 1 340 840A290 290 0 0 1 340 260zM340 492A58 58 0 0 1 340 608A58 58 0 0 1 340 492z" stroke-width="4" stroke="rgb(0,0,0)" fill="rgb(0,0,255)">
        <title>This will only display on the donut</title>
    </path>
</svg>
GHC
  • 2,658
  • 5
  • 30
  • 35
1

This is the classic donut shape I'm not sure if you are trying to achieve this with standard SVG or JavaScript that produces SVG The objective can be achieved by including a relative "moveto" command in a single path definition

And click "donut holes" on the right side of the interactive examples. At the very least you can see the path definition that made the red donut.

Chasbeen
  • 1,448
  • 12
  • 17
  • Thanks, this helped. It was actually a pain to figure out exactly (and the outer stroke is about 1 unit from being closed, which is alright in my book), but I figured out the syntax for making a ring of arbitrary center point and outer and inner radiuses from this – DragonJawad Jun 17 '16 at 13:15
-2

Here's a routine to create a bezier arc which is as close as makes no odds to a circle. You need four of them in a path for a complete circle.

 BezierCurve BezierArc(double ox, double oy, double r, double thetaa, double thetab)
    {
        double theta;
        double cpx[4];
        double cpy[4];
        int i;
        int sign = 1;

        while (thetaa > thetab)
            thetab += 2 * Pi;
        theta = thetab - thetaa;
        if (theta > Pi)
        {
            theta = 2 * Pi - theta;
            sign = -1;
        }
        cpx[0] = 1;
        cpy[0] = 0;
        cpx[1] = 1;
        cpy[1] = 4.0 / 3.0 * tan(theta / 4);
        cpx[2] = cos(theta) + cpy[1] * sin(theta);
        cpy[2] = sin(theta) - cpy[1] * cos(theta);
        cpx[3] = cos(theta);
        cpy[3] = sin(theta);
        cpy[1] *= sign;
        cpy[2] *= sign;
        cpy[3] *= sign;

        for (i = 0; i < 4; i++)
        {
            double xp = cpx[i] * cos(thetaa) + cpy[i] * -sin(thetaa);
            double yp = cpx[i] * sin(thetaa) + cpy[i] * cos(thetaa);
            cpx[i] = xp;
            cpy[i] = yp;
            cpx[i] *= r;
            cpy[i] *= r;
            cpx[i] += ox;
            cpy[i] += oy;
        }

        return BezierCurve({cpx[0], cpy[0]},{cpx[1], cpy[1]}, {cpx[2], cpy[2]}, {cpx[3], cpy[3]});
    }
Malcolm McLean
  • 6,258
  • 1
  • 17
  • 18
  • Quite a long function to get the same results as the other answers.. Plus OP wanted a SVG solution – Philip Feb 23 '17 at 22:08