Search code examples
htmlsvg

SVG circle, what is the radius percentage of?


I have the following code:

.container{
    
    width: 100px;
    height: 200px;
    
    background-color: red;
    
    resize: both;     /* Just to make it... */
    overflow: hidden; /* ...resizable for testing. */
    
}
<div class="container">
    <svg width="100%" height="100%">
        <circle cx="50%" cy="50%" r="25%" fill="black"></circle>
    </svg>
</div>

As I resize .container, I can easily see that:

  • cx is percentage of the width
  • cy is percentage of the height

But I cannot understand what r is percentage of. What is r percentage of?


Solution

  • As commented by Roko C. Buljan when using % units the relative value refers to the normalized diagonal length.

    See the W3C SVG 2 specs (working draft) "8.9. Units"

    For any other length value expressed as a percentage of the SVG viewport, the percentage must be calculated as a percentage of the normalized diagonal of the ‘viewBox’ applied to that viewport. If no ‘viewBox’ is specified, then the normalized diagonal of the SVG viewport must be used. The normalized diagonal length must be calculated with sqrt((width)**2 + (height)**2)/sqrt(2).

    here's an example

    let w = 100;
    let h = 200;
    let c = Math.sqrt(w ** 2 + h ** 2) / Math.sqrt(2)
    let rAbsolute = c * 0.25
    
    // get absolute radius via baseVal
    let r = c1.r.baseVal.value
    console.log(rAbsolute, r)
    .container {
      width: 100px;
      height: 200px;
      background-color: red;
    }
    <div class="container">
      <svg width="100%" height="200px">
            <circle id="c1" cx="50%" cy="50%" r="25%" fill="#ccc"></circle>
            <circle cx="50%" cy="50%" r="39.52847075210474" fill="none" stroke="blue" />
        </svg>
    </div>

    You can also check your calculations using the baseVal property from the SVGAnimatedLength interface.

    If you actually need the circle scale more predictably you may also add a square-shaped viewBox. In this case the preserveAspectRatio (in the example below the default "xMidYMid meet") attribute will affect the alignment and scaling.

    .container {
      width: 100px;
      height: 200px;
      background-color: red;
      resize: both;
      overflow: hidden;
    }
    <div class="container">
        <svg width="100%" height="100%" viewBox="0 0 100 100" preserveAspectRatio="xMidYMid meet">
          <rect width="100%" height="100%" fill="none" stroke="#ccc"/>
            <circle cx="50%" cy="50%" r="25%" fill="black" />
        </svg>
    </div>

    As you can see the radius of the circle stays at 25% relative to the 100x100 viewBox (so the circle stays 50% wide).