Skip to content

Fill Percentage area in a custom Oval SVG image

I would like to fill a custom SVG to a specific percentage.

Here is my initial SVG

<svg width="202" height="195" viewBox="0 0 202 195" fill="none" xmlns="http://www.w3.org/2000/svg">
<path opacity="0.1" d="M96.8166 4.06964C16.0794 8.40606 -20.4645 94.8546 20.2957 157.019C54.6867 204.16 143.361 202.123 184.273 150.807C226.464 97.5789 163.505 0.38025 96.8166 4.06964Z" stroke="#313848" stroke-width="6.87634" stroke-miterlimit="10" stroke-linecap="round" stroke-linejoin="round"/>
</svg>

Suppose there is a progress of x% so I would like to fill this SVG like

<svg width="207" height="203" viewBox="0 0 207 203" fill="none" xmlns="http://www.w3.org/2000/svg">
<path opacity="0.1" d="M99.8166 12.0696C19.0794 16.4061 -17.4645 102.855 23.2957 165.019C57.6867 212.16 146.361 210.123 187.273 158.807C229.464 105.579 166.505 8.38025 99.8166 12.0696Z" stroke="#313848" stroke-width="6.87634" stroke-miterlimit="10" stroke-linecap="round" stroke-linejoin="round"/>
<path d="M99.8142 12.0736C166.502 8.38527 229.463 105.585 187.273 158.812" stroke="#EA7052" stroke-width="6.87634" stroke-miterlimit="10" stroke-linecap="round" stroke-linejoin="round"/>
<path d="M96.1683 2.4287C88.1789 2.85671 84.5529 11.2658 88.579 17.3074C91.9765 21.8887 100.751 21.6836 104.805 16.6905C108.986 11.5113 102.768 2.06471 96.1683 2.4287Z" fill="#EDEDEE" stroke="#EA7052" stroke-width="4.76054" stroke-miterlimit="10" stroke-linecap="round" stroke-linejoin="round"/>
<path d="M171.545 162.236C169.583 169.548 177.007 175.33 184.329 173.522C189.985 171.84 192.408 163.889 188.57 158.747C184.582 153.434 173.156 156.193 171.545 162.236Z" fill="#EDEDEE" stroke="#EA7052" stroke-width="4.76054" stroke-miterlimit="10" stroke-linecap="round" stroke-linejoin="round"/>
</svg>

I am not able to figure out how to achieve this. I want it to be dynamic so that I can make it for any percentage.

Also, I need to animate it from the starting point to the endpoint in a circular motion.

Any help would be highly appreciatable.

Answer

As I’ve commented:

You can calculate the length of the path using the getTotalLength() method. This represents 100%.

Next you can get the length representing the x% (xperc in the code).

Now you can use stroke-dasharray to represent the partial path.

You can calculate the position of the last point using the getPointAtLength() method.

Please read the comments in my code.

//the desired percentege
let xperc = .35;
//the total length of the path
let tl = base.getTotalLength();
//the partial length at the given percentage xperc
let partial = tl * xperc;

//set the stroke-dasharray of the second use element
perc.setAttribute("stroke-dasharray", `${partial} ${tl -partial}`)

//calculate the position of the point marking the end position
let theEnd = base.getPointAtLength(partial);
// set the cx and the cy attributes for the end point
end.setAttribute("cx", theEnd.x);
end.setAttribute("cy", theEnd.y);
circle {
  stroke: red;
  fill:white;
  stroke-width: 6.87634;
}
<svg width="207" height="203" viewBox="0 0 207 203" fill="none" xmlns="http://www.w3.org/2000/svg">
  <defs>
<path id="base" d="M99.8166,12.0696L99.8166,12.0696C166.505,8.38025 229.464,105.579 187.273,158.807C146.361,210.123 57.6867,212.16 23.2957,165.019C-17.4645,102.855 19.0794,16.4061 99.8166,12.0696Z" stroke-width="6.87634" stroke-miterlimit="10" stroke-linecap="round" stroke-linejoin="round"/>
  </defs>
<use xlink:href="#base" stroke="silver" />
<use xlink:href="#base" stroke="red" id="perc" />
  
<circle cx="99.8166" cy="12.0696" r="10" />
<circle id="end" r="10" />
</svg>

OBSERVATION: since your path goes counter clockwise I had to reverse the path to get the desired result

And this is an example where I’m using an input type range to change the percent value:

let xperc = itr.value;
onInput();


itr.addEventListener("input", onInput)

function onInput() {
  xperc = itr.value;
  
  let tl = base.getTotalLength();
  let partial = tl * xperc;

  perc.setAttribute("stroke-dasharray", `${partial} ${tl - partial}`);

  let theEnd = base.getPointAtLength(partial);

  end.setAttribute("cx", theEnd.x);
  end.setAttribute("cy", theEnd.y);
}
circle {
  stroke: red;
  fill:white;
  stroke-width: 6.87634;
}
<input id="itr" type="range" min="0" max="1" step=".001" value=".35" /><br>

<svg width="207" viewBox="-5 -5 220 220" fill="none" xmlns="http://www.w3.org/2000/svg">
  <defs>
<path id="base" d="M99.8166,12.0696L99.8166,12.0696C166.505,8.38025 229.464,105.579 187.273,158.807C146.361,210.123 57.6867,212.16 23.2957,165.019C-17.4645,102.855 19.0794,16.4061 99.8166,12.0696Z" stroke-width="6.87634" stroke-miterlimit="10" stroke-linecap="round" stroke-linejoin="round"/>
  </defs>
<use xlink:href="#base" stroke="silver" />
<use xlink:href="#base" stroke="red" id="perc" />
  
<circle cx="99.8166" cy="12.0696" r="10" />
<circle id="end" r="10" />
</svg>

And another demo where I’m using javascript to animate it from 0 to 1:

//the animation begins at 0
let xperc = 0;
//get the total length of the path
let tl = base.getTotalLength();
//the request animation id
let rid = null;



function Animation() {
  rid = window.requestAnimationFrame(Animation);
  // while xperc < 1 increase it's value by 0.001. Else stop the animation
  if (xperc < 1) {
    xperc += 0.001;
  }else{window.cancelAnimationFrame(rid)}

  //the same as in the first example
  let partial = tl * xperc;
  perc.setAttribute("stroke-dasharray", `${partial} ${tl - partial}`);

  let theEnd = base.getPointAtLength(partial);

  end.setAttribute("cx", theEnd.x);
  end.setAttribute("cy", theEnd.y);
}

Animation();
circle {
  stroke: red;
  fill:white;
  stroke-width: 6.87634;
}
<svg width="207" viewBox="-5 -5 220 220" fill="none" xmlns="http://www.w3.org/2000/svg">
  <defs>
<path id="base" d="M99.8166,12.0696L99.8166,12.0696C166.505,8.38025 229.464,105.579 187.273,158.807C146.361,210.123 57.6867,212.16 23.2957,165.019C-17.4645,102.855 19.0794,16.4061 99.8166,12.0696Z" stroke-width="6.87634" stroke-miterlimit="10" stroke-linecap="round" stroke-linejoin="round"/>
  </defs>
<use xlink:href="#base" stroke="silver" />
<use xlink:href="#base" stroke="red" id="perc" />
  
<circle cx="99.8166" cy="12.0696" r="10" />
<circle id="end" r="10" />
</svg>