Home > Net >  Segemented SVG Outlined Circle
Segemented SVG Outlined Circle

Time:04-15

Thanks in advance for your help!

I am trying to build an SVG circle which has no fill only an outline and is split into 3 segments.

I have managed to find a thread that shows me how to split a circle into 4 segments (see snippet) but I am very new to SVG's so I don't really know what is going on and how to get this to 3 segments and only the outline.

I have attached a screenshot showing the outcome of the circle. I don't want any visible sign of the segments but I want to be able to use each segment separately. (Basically I am going to have a page where the circle completes itself as you scroll down the page.)

Segemnted Circle

        <svg width="200" height="200" viewBox="0 0 200 200">
          <g transform="translate(100,100)" stroke="#000" stroke-width="2">
            <path d="M0 0-70 70A99 99 0 0 1-70-70Z" fill="none"/>
            <path d="M0 0-70-70A99 99 0 0 1 70-70Z" fill="none"/>
            <path d="M0 0 70-70A99 99 0 0 1 70 70Z" fill="none"/>
            <path d="M0 0 70 70A99 99 0 0 1-70 70Z" fill="none"/>
          </g>
        </svg>

CodePudding user response:

You will need to calculate the starting and the ending point of every arc. Since you want 3 arcs the angle used is 1/3 of a circle i.e 2*Math.PI/3.

Please read the comments in the code.

//the circle's radius
let r = 70;
//points used for the start and end of every arc
let points =[];
//the paths
let ps = document.querySelectorAll("path");

//calculating the points used for the start and end of every arc
for(let angle = -Math.PI/2; angle < 2*Math.PI; angle  = 2*Math.PI/3 ){
  let point = {}
  point.x = r * Math.cos(angle);
  point.y = r * Math.sin(angle);
  points.push(point)
}

//defining the valueof the d attribute of every path as an arc with the given radius, starting at the previous point and ending at the actual point
for(let i = 0,l=points.length; i<l-1; i  ){
  let point = points[i 1];
  let prev = points[i]
  let d = `M${prev.x},${prev.y} A${r},${r},0,0,1,${point.x},${point.y}`
  //setting the d attribute
  ps[i].setAttribute("d",d);
}
svg{border:solid}
<svg viewBox="-100 -100 200 200" width="300" fill="none" stroke-width="20">
  <path stroke="gold"/>
  <path stroke="skyBlue"/>
  <path stroke="tomato"/>
</svg>

CodePudding user response:

Instead of all Math, let a native Web Component <svg-segments> do the work.
The important part in the created SVG <path> are the pathLength and stroke-dasharray values

All you specify, is a string of colors defining how many and what color segments are drawn.
A none color will create an empty segment:

<svg-segments parts="red,,blue"></svg-segments>

In the SO snippet below; click the circles to add more segments

<div style="display:grid;grid-template-columns:repeat(5,1fr);gap:10px;background:grey">
  <svg-segments parts="red,green,blue"></svg-segments>
  <svg-segments parts="red,,blue"></svg-segments>
  <svg-segments parts=",,red"></svg-segments>
  <svg-segments parts="red,green,blue,purple" width="5"></svg-segments>
  <svg-segments parts="red,,,green,,blue,,red,red" width="50"></svg-segments>
</div>
<script>
  customElements.define("svg-segments", class extends HTMLElement {
    static get observedAttributes() {
      return ["parts", "size"]; // svg is redrawn at *every* attribute update!
    }
    attributeChangedCallback() {
      this.parts = this.getAttribute("parts").split(",");
      let vb = 200; // viewPort size; might need to tweak this
      let width = this.getAttribute("width") || 25; // stroke-width
      let a = vb / 2 - width/2; // calculate circle arcs
      let b = vb - width;
      this.innerHTML = `<svg viewBox='0 0 ${vb} ${vb}'>`  
        this.parts.map((col, idx) => {
          if (col)
            return `<path d='M${width/2},${vb/2}a${a},${a} 0 1,0 ${b},0a${a},${a} 0 1,0 -${b},0' 
            fill='none' stroke-width='${width}' pathLength='${this.parts.length}'
            stroke='${col}' stroke-dashoffset='${idx}'
            stroke-dasharray='1 ${this.parts.length-1}'/>`;
          else return ``;
        })   `</svg>`;
    }
    connectedCallback() {// for StackOverflow Snippet demo purpose only
      this.onclick = (evt) => { 
        let moreparts = this.parts.join(",")   ",red";
        this.setAttribute("parts", moreparts);
      }
    }
  })
</script>

SVG has some quirks calculating path-arcs, you may need to tweak the viewPort dimensions to a higher dimension when you see artifacts (red):

Or re-factor to use <circle>

  • Related