Simple svg css progress circle - html

I am trying to look for a way to achieve a simple progress circle (static) with no animations. The examples I have found have very different offsets for percentage such as given in the example below. How do I make my progress circle in such a way that if I provide offset as 50%, then it is exactly 50% (half filled)?
.u-absoluteCenter {
position: absolute;
top: 0;
right: 0;
bottom: 0;
left: 0;
margin: auto;
}
.u-flexCenter {
display: flex;
align-items: center;
justify-content: center;
}
.u-offscreen {
position: absolute;
left: -999em;
}
.demo {
position: absolute;
top: 0;
right: 0;
bottom: 0;
left: 0;
margin: auto;
display: flex;
align-items: center;
justify-content: center;
}
.progress {
transform: rotate(-90deg);
}
.progress__value {
stroke-dasharray: 0;
stroke-dashoffset: 0;
}
#-webkit-keyframes progress {
from {
stroke-dashoffset: 339.292;
}
to {
stroke-dashoffset: 0;
}
}
#keyframes progress {
from {
stroke-dashoffset: 339.292;
}
to {
stroke-dashoffset: 0;
}
}
<svg width="120" height="120" viewBox="0 0 120 120">
<circle cx="60" cy="60" r="54" fill="none" stroke="#e6e6e6" stroke-width="12" />
<circle cx="60" cy="60" r="54" fill="none" stroke="#f77a52" stroke-width="12"
stroke-dasharray="339.292" stroke-dashoffset="339.292" />
</svg>

You can leverage an SVG attribute to set the path length rather than having to calculate it.
pathLength sets the length to whatever you need...say 100 for a progress bar.
The pathLength attribute lets authors specify a total length for the path, in user units. This value is then used to calibrate the browser's distance calculations with those of the author, by scaling all distance computations using the ratio pathLength/(computed value of path length).
pathLength="100"
Then you can set the stroke-dasharray to 100 as well and then adjust the stroke-dashoffset as needed....
::root {
--val: 0;
}
svg {
transform: rotate(-90deg);
}
.percent {
stroke-dasharray: 100;
stroke-dashoffset: calc(100 - var(--val));
}
.fifty {
--val: 50;
}
.sixty {
--val: 60;
}
.ninety {
--val: 90;
}
<svg width="120" height="120" viewBox="0 0 120 120">
<circle cx="60" cy="60" r="54" fill="none" stroke="#e6e6e6" stroke-width="12" />
<circle class="percent fifty" cx="60" cy="60" r="54" fill="none" stroke="#f77a52" stroke-width="12" pathLength="100" />
</svg>
<svg width="120" height="120" viewBox="0 0 120 120">
<circle cx="60" cy="60" r="54" fill="none" stroke="#e6e6e6" stroke-width="12" />
<circle class="percent sixty" cx="60" cy="60" r="54" fill="none" stroke="#f77a52" stroke-width="12" pathLength="100" />
</svg>
<svg width="120" height="120" viewBox="0 0 120 120">
<circle cx="60" cy="60" r="54" fill="none" stroke="#e6e6e6" stroke-width="12" />
<circle class="percent ninety" cx="60" cy="60" r="54" fill="none" stroke="#f77a52" stroke-width="12" pathLength="100" />
</svg>

As Paulie says, pathLength is the key to progress circles
A modern Custom Element (supported in all modern browsers) makes for a re-usable HTML Element
<svg-progress-circle percent="30"></svg-progress-circle>
<svg-progress-circle percent="20" color="blue"></svg-progress-circle>
<svg-progress-circle percent="80" color="gold"></svg-progress-circle>
Added a range-input for interactive demo purposes.
Percent is a property on the element, you can set with code like:
document.getElementById("Slider1").percent = <PERCENTAGE>;
If you don't want a dashed grey fullcircle, delete the dash setting from the pathLenght=120 path
I used a path instead of overlapping circles because with some other settings the almost same code can create pie-charts.
<style>
svg { width: 150px; background: teal }
svg-progress-circle[percent="100"] path { stroke: green }
</style>
<svg-progress-circle percent="30"></svg-progress-circle>
<svg-progress-circle percent="20" color="blue"></svg-progress-circle>
<svg-progress-circle percent="80" color="gold"></svg-progress-circle>
<script>
customElements.define("svg-progress-circle", class extends HTMLElement {
connectedCallback() {
let d = 'M5,30a25,25,0,1,1,50,0a25,25,0,1,1,-50,0'; // circle
this.innerHTML =
`<input type="range" min="0" max="100" step="10" value="30"`+ // delete 2 lines
` oninput="this.parentNode.percent=this.value" /><br>`+ // just for demo
`<svg viewBox="0 0 60 60">
<path stroke-dasharray="10 2" stroke-dashoffset="-19"
pathlength="120" d="${d}" fill="grey" stroke="lightgrey" stroke-width="5"/>
<path stroke-dasharray="30 70" stroke-dashoffset="-25"
pathlength="100" d="${d}" fill="none"
stroke="${this.getAttribute("color")||"red"}" stroke-width="5"/>
<text x="50%" y="57%" text-anchor="middle">30%</text></svg>`;
this.style.display='inline-block';
this.percent = this.getAttribute("percent");
}
set percent(val = 0) {
this.setAttribute("percent", val);
let dash = val + " " + (100 - val);
this.querySelector("path+path").setAttribute('stroke-dasharray', dash);
this.querySelector("text").innerHTML = val + "%";
this.querySelector("input").value = val;
}
})
</script>
Note: I am working on a complete Web Component that does Pie Graphs and fancy Progress circles like:
But, it is one of many side-projects... HTML examples and obfuscated source code available at https://pie-meister.github.io/

Related

How can I translate an element and at the same time rotate realistically?

It turns out of position and moves incorrectly. It's hard to find the exact point so it looks realistic and the transitions are smooth. the turning is out of cue and can not find a solution to make it turn faster as it moves at its normal rate. I have tried many times to get the exact timing but I cant find a way.
function wid() {
if (window.innerWidth < 960) {
var r = document.querySelector(':root');
r.style.setProperty('--wid', '100%');
}
wid();
}
#car {
background-color: transparent;
width: 10px;
height: 20px;
transform: translate(241.5px, 215px);
position: relative;
animation-name: driving;
animation-duration: 10s;
animation-timing-function: linear;
/*linear ease-in-out*/
}
#map {
background-color: transparent;
width: 400px;
margin: auto;
position: absolute;
transform: translate(5%, 5%);
}
#bod {
background-color: green;
position: absolute;
width: 100%;
height: 100%;
}
html,
body {
margin: 0px;
padding: 0px;
width: 100%;
height: 100%;
}
.mainsec {}
#keyframes driving {
0% {
transform: translate(241.5px, 215px);
}
10% {
transform: translate(241.5px, 40px)rotate(-60deg);
}
28% {
transform: translate(41.5px, 86px)rotate(-125deg);
}
38% {
transform: translate(41.5px, 300px)rotate(-180deg);
}
40% {
transform: translate(120px, 360px)rotate(-220deg);
}
50% {
transform: translate(160px, 360px)rotate(-125deg);
}
60% {
transform: translate(200px, 450px)rotate(-230deg);
}
70% {
transform: translate(400px, 460px)rotate(-360deg);
}
80% {
transform: translate(260.5px, 215pxrotate(360deg));
}
90% {
transform: translate(241.5px, 180pxrotate(360deg));
}
100% {
transform: translate(241.5px, 215pxrotate(360deg));
}
}
<div id="bod">
<div id="map">
<svg version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" width="415.00774" height="464.03421" viewBox="0,0,415.00774,464.03421">
<g transform="translate(-5.56354,24.30861)">
<g data-paper-data="{"isPaintingLayer":true}" fill-rule="nonzero" stroke-width="0" stroke-linecap="butt" stroke-linejoin="miter" stroke-miterlimit="10" stroke-dasharray="" stroke-dashoffset="0" style="mix-blend-mode: normal">
<path d="M193.21864,323.70241c2.08101,14.58763 -0.75936,39.6034 -0.75936,39.6034l-76.95053,-59.65507c0,0 27.04231,-21.9967 43.16349,-20.31537c5.01955,0.5235 15.96324,3.86994 22.75063,11.80093c6.7874,7.93099 11.43447,26.03343 11.79578,28.56611z" fill="#e2ea8f" stroke="none"/>
<path d="M5.56688,290.21195c0.51544,-15.25718 8.62251,-39.2653 8.62251,-39.2653l75.02651,85.4148c0,0 -34.79132,12.85441 -52.63063,5.75382c-5.55452,-2.21087 -17.2321,-9.2315 -23.32635,-19.42903c-6.09425,-10.19755 -7.78154,-29.82536 -7.69204,-32.47428z" fill="#e2ea8f" stroke="none"/>
<path d="M219.05583,250.34734v-212.80012c0,0 1.48764,-14.0667 -3.9272,-17.72172c-5.41484,-3.65502 -26.47434,-0.83576 -26.47434,-0.83576l-66.30684,14.95228c0,0 -31.54844,6.49453 -43.16799,13.0135c-11.85598,6.65162 -17.65815,11.05621 -23.81015,25.35199c-6.15201,14.29579 -4.49552,155.45874 -4.49552,155.45874c0,0 0.12847,18.24631 2.32244,26.68213c1.97899,7.60918 18.15757,34.8776 33.34816,40.17712c19.19535,6.69665 43.22077,-3.09306 62.42146,3.17404c13.17719,4.30105 19.38737,8.26387 26.48485,18.94892c8.15443,12.27627 12.12432,31.97154 14.43416,37.56194c3.22503,7.80536 6.42812,17.86233 6.42812,17.86233l3.4807,18.08984c0,0 1.03251,15.13972 8.75099,18.80522c6.38845,3.03387 114.48707,3.64998 151.55195,3.77223c7.71664,0.02545 10.45146,1.27859 12.93151,-2.85614c1.26925,-2.11608 1.73211,-6.23932 -2.62668,-9.76101c-4.15809,-3.35953 -50.05552,-4.44025 -50.05552,-4.44025l8.83274,-34.13795c0,0 28.4294,-1.70463 51.65944,4.28594c23.23005,5.99057 28.02805,8.6783 34.85366,27.38878c4.72033,16.40398 10.64834,45.16217 -7.11765,45.35982c-60.58922,0.67405 -196.5872,1.94573 -217.63794,0.05149c-6.61359,-0.59513 -10.36459,-4.25214 -10.90643,-5.25027c-2.18537,-4.02565 -3.47544,-7.21102 -4.98448,-12.09238c-1.50902,-4.88135 -11.88057,-53.84601 -20.93852,-71.76255c-6.71889,-13.2899 -21.01689,-13.36816 -21.01689,-13.36816l-36.99995,-0.31926c0,0 -24.73763,1.62251 -39.7364,-10.8668c-11.6551,-9.70507 -19.2506,-18.21353 -27.99162,-32.54346c-8.74102,-14.32992 -9.25637,-22.76613 -12.14607,-33.25777c-2.88029,-10.45743 -3.43264,-33.62966 -3.43264,-33.62966c0,0 2.31202,-92.96558 1.9462,-152.4744c-0.07076,-11.51164 3.51657,-14.76709 6.12168,-20.3207c3.4054,-7.25967 10.81182,-13.80213 10.81182,-13.80213c0,0 19.26399,-15.40827 29.93019,-21.31454c10.50848,-5.81894 33.60085,-13.86497 33.60085,-13.86497l94.80812,-23.31008c0,0 15.46605,-4.62962 28.36495,-4.85435c11.96737,-0.2085 21.24293,3.77639 25.88139,8.78947c2.73367,2.95445 8.45832,9.73142 10.28464,14.53415c4.2386,11.14628 2.66852,21.78451 2.66852,21.78451v229.54004c0,0 -1.40764,17.5481 -0.50008,26.82662c0.34086,3.48486 4.70635,31.95634 21.05493,49.25219c15.8564,16.77515 51.49572,35.54486 51.49572,35.54486l-8.29877,33.81234c0,0 -16.52481,-0.78157 -32.88652,-13.95939c-14.69724,-7.65388 -27.17248,-17.57315 -36.41394,-27.55132c-16.40866,-17.71676 -22.39766,-35.61914 -24.07827,-41.35593c-5.39145,-18.40375 -8.51881,-62.56938 -8.51881,-62.56938z" fill="#414141" stroke="#000000"/>
<path d="M219.05583,165.09336v-5.97191h38.14575v5.97191z" fill="#d2d2d2" stroke="#000000"/>
<path d="M224.72917,199.44555v-1.40716h10.52976v1.40716z" fill="#d2d2d2" stroke="#000000"/>
<path d="M240.9985,199.47883v-1.40716h10.52976v1.40716z" fill="#d2d2d2" stroke="#000000"/>
<path d="M224.72915,169.62976v-1.40716h10.52976v1.40716z" fill="#d2d2d2" stroke="#000000"/>
<path d="M240.99849,169.66305v-1.40716h10.52976v1.40716z" fill="#d2d2d2" stroke="#000000"/>
<path d="M224.72915,184.69724v-1.40716h10.52976v1.40716z" fill="#d2d2d2" stroke="#000000"/>
<path d="M240.9985,184.73053v-1.40716h10.52976v1.40716z" fill="#d2d2d2" stroke="#000000"/>
<path d="M224.73546,228.96504v-1.40716h10.52976v1.40716z" fill="#d2d2d2" stroke="#000000"/>
<path d="M241.00479,228.99833v-1.40716h10.52976v1.40716z" fill="#d2d2d2" stroke="#000000"/>
<path d="M224.73546,214.21673v-1.40716h10.52976v1.40716z" fill="#d2d2d2" stroke="#000000"/>
<path d="M241.00479,214.25002v-1.40716h10.52976v1.40716z" fill="#d2d2d2" stroke="#000000"/>
<path d="M203.48125,23.23113l-0.09191,-5.49181c0,0 9.04848,-1.64122 13.01789,1.24562c2.86101,2.08074 4.05979,12.06922 4.05979,12.06922l-6.28979,0.0411c0,0 -0.42661,-5.56759 -3.14675,-7.23476c-2.27102,-1.39192 -7.54922,-0.62936 -7.54922,-0.62936z" fill="#bd0000" stroke="none"/>
<path d="M207.96794,23.16086v-5.85189c0,0 2.70134,0.06002 4.01174,0.21819c1.25881,0.15192 3.70211,0.71279 3.70211,0.71279l-4.38891,5.71888c0,0 -1.13567,-0.40556 -1.69516,-0.53983c-0.5486,-0.13166 -1.62977,-0.25815 -1.62977,-0.25815z" fill="#bdbdbd" stroke="none"/><path d="M214.08583,31.1407l-0.66499,-3.85692l5.98488,-1.59597l0.93098,5.18691z" fill="#bdbdbd" stroke="none"/>
</g>
</g>
</svg>
</div>
<div id="car">
<svg version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" width="5.69151" height="13.00614" viewBox="0,0,5.69151,13.00614">
<g transform="translate(-237.15425,-173.49693)">
<g data-paper-data="{"isPaintingLayer":true}" fill-rule="nonzero" stroke-width="0" stroke-linecap="butt" stroke-linejoin="miter" stroke-miterlimit="10" stroke-dasharray="" stroke-dashoffset="0" style="mix-blend-mode: normal">
<path d="M237.95271,176.91509v-0.36484h1.65836v0.36484z" fill="#7e7e7e" stroke="none"/><path d="M240.4616,177.96722l-0.10174,-0.19425l1.68345,-0.8818l0.10175,0.19425z" data-paper-data="{"index":null}" fill="#7e7e7e" stroke="none"/>
<path d="M240.40631,176.94827v-0.36484h1.65836v0.36484z" data-paper-data="{"index":null}" fill="#7e7e7e" stroke="none"/>
<path d="M237.87231,177.05225l0.10177,-0.19425l1.68345,0.8818l-0.10174,0.19425z" fill="#7e7e7e" stroke="none"/>
<path d="M237.68777,185.93213l0.07908,-4.54285l0.04206,-0.67016l0.26596,-0.28733c0,0 0.87932,-0.26451 1.13707,-0.61548c0.38158,-0.51961 0.30716,-1.61303 0.30716,-1.61303l0.13902,-3.00375l-0.57115,-0.00965l-0.80751,0.2432l-0.79069,-0.00484v-1.58791h2.20561l0.33641,-0.34338l0.34463,0.34338h2.13582v1.58791l-0.66608,0.00627l-0.80156,-0.27803l-0.63165,-0.00276l0.13298,3.04754c0,0 -0.04796,1.08621 0.37004,1.67854c0.27203,0.38548 1.01745,0.57779 1.01745,0.57779c0,0 0.22801,0.19788 0.25867,0.29475c0.03066,0.09686 0.03601,0.63613 0.03601,0.63613l0.08511,4.54371z" fill="#000000" stroke="#000000"/>
<path d="M237.15425,177.61095v-1.79988h0.82698v1.79988z" fill="#353535" stroke="none"/>
<path d="M242.01878,177.65959v-1.79987h0.82697v1.79987z" fill="#353535" stroke="none"/>
<path d="M237.15425,186.26759v-1.79987h1.0923v1.79987z" fill="#353535" stroke="none"/>
<path d="M241.75344,186.31622v-1.79986h1.0923v1.79986z" fill="#353535" stroke="none"/>
<path d="M238.35111,185.93798l0.0268,-1.54014l-0.65122,-0.69631l0.04014,-2.30639l0.04206,-0.67016l0.26596,-0.28734c0,0 0.87933,-0.26451 1.13708,-0.61549c0.38158,-0.51961 0.30716,-1.61302 0.30716,-1.61302l0.13902,-3.00375l0.03627,-1.35921l0.33641,-0.34338l0.34463,0.34338l0.03653,1.31338l0.13297,3.04753c0,0 -0.04796,1.08623 0.37004,1.67854c0.27203,0.38548 1.01745,0.57779 1.01745,0.57779c0,0 0.22801,0.19788 0.25867,0.29475c0.03066,0.09686 0.03602,0.63613 0.03602,0.63613l0.0426,2.27394l-0.64783,0.79572l0.06016,1.47404z" fill="#f30000" stroke="#000000"/>
<path d="M238.82256,186.50307v-0.92868h2.35487v0.92868z" fill="#000000" stroke="none"/>
<path d="M239.43365,180.99989v-1.46517c0,0 0.25408,-0.66824 0.56634,-0.65782c0.34514,0.01152 0.56635,0.65782 0.56635,0.65782v1.46517z" fill="#7e7e7e" stroke="none"/>
<path d="M239.99999,180.68103c-0.17989,0 -0.32574,-0.16918 -0.32574,-0.37787c0,-0.20869 0.14583,-0.37787 0.32574,-0.37787c0.17989,0 0.32575,0.16918 0.32575,0.37787c0,0.20869 -0.14584,0.37787 -0.32575,0.37787z" fill="#f30000" stroke="none"/>
</g>
</g>
</svg>
</div>
</div>

Pie chart using circle element

I am trying to create a pie chart using circle element in svg. I am able to fill values to 60%, 30% and 10% but all the circle starting from same position.
How can I make next circle start from where previous one ended?
svg { transform: rotate(-90deg); }
circle {
stroke-width: 3;
stroke-opacity: 1;
fill: none;
}
circle.stroke-yellow {
stroke: yellow;
stroke-dasharray: calc(2*3.14*50*60/100),calc(2*3.14*50);
}
circle.stroke-red {
stroke: red;
stroke-dasharray: calc(2*3.14*50*30/100),calc(2*3.14*50);
}
circle.stroke-blue {
stroke: blue;
stroke-dasharray: calc(2*3.14*50*10/100),calc(2*3.14*50);
}
<svg xmlns="http://www.w3.org/2000/svg" height="220">
<circle class="stroke-yellow" cy="110" cx="110" r="50"></circle>
<circle class="stroke-red" cy="110" cx="110" r="50"></circle>
<circle class="stroke-blue" cy="110" cx="110" r="50"></circle>
</svg>
Also stroke-width is not working which I mentioned in CSS.
As #enxaneta mentioned: you will need to give each pie segment an offset by changing the dash-offset property.
Based on your code example:
svg {
transform: rotate(-90deg);
}
circle {
stroke-width: 3;
stroke-opacity: 1;
fill: none;
}
.stroke {
stroke-width: 100;
--circumference: 314.159
}
circle.stroke-blue {
stroke: blue;
stroke-dasharray: calc( var(--circumference) * 10 / 100), var(--circumference);
stroke-dashoffset: 0;
}
circle.stroke-red {
stroke: red;
stroke-dasharray: calc( var(--circumference) * 30 / 100), var(--circumference);
stroke-dashoffset: calc( 0 - var(--circumference) * 10 / 100);
}
circle.stroke-yellow {
stroke: yellow;
stroke-dasharray: calc( var(--circumference) * 60 / 100), var(--circumference);
stroke-dashoffset: calc( 0 - var(--circumference) * 40 / 100);
}
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 220 220" height="220">
<circle class="stroke stroke-blue stroke-10" cy="110" cx="110" r="50" />
<circle class="stroke stroke-yellow stroke-60" cy="110" cx="110" r="50" />
<circle class="stroke stroke-red stroke-30" cy="110" cx="110" r="50" />
</svg>
stroke-width needs to be '100' (radius*2);
Drawbacks:
Firefox seems to have problems, rendering svg elements when attributes are are calculated via css calc() (SVG pie-chart working only in Chrome, not in Firefox)
difficult to be reused for multiple pi chart instances
Recommendations:
simplify your calculations by optimizing your svg geometry.
control your values (e.g pie percentages, colors) via HTML/svg attributes (or css variables)
Example showing 2 slightly different svg setups:
body{
font-family: arial;
font-size:10px;
}
.icon-wrp {
position: relative;
display: inline-block;
width: 200px;
vertical-align: top;
}
.icon-wrp p{
font-size:12px;
}
<!--simple pi -->
<div class="icon-wrp">
<svg class="svgPieAsset" viewBox="0 0 63.6619772368 63.6619772368">
<symbol id="slice">
<circle transform="rotate(-90 31.8309886184 31.8309886184)" id="circle" class="percent" cx="50%" cy="50%" r="15.9154943092" fill="none" stroke-width="31.8309886184" />
</symbol>
<!--actual pi slices -->
<use class="segment" href="#slice" stroke="green" stroke-dashoffset="0" stroke-dasharray="30 100" />
<use class="segment" href="#slice" stroke="orange" stroke-dashoffset="-30" stroke-dasharray="60 100" />
<use class="segment" href="#slice" stroke="purple" stroke-dashoffset="-90" stroke-dasharray="10 100" />
</svg>
<p>1. Precice geometry based on PI. <br>Should be rendered fine on all browsers.</p>
</div>
<div class="icon-wrp">
<svg class="svgPieAsset" viewBox="0 0 100 100">
<symbol id="slice2">
<circle transform="rotate(-90 50 50)" id="circle" class="percent" cx="50%" cy="50%" r="25" fill="none" stroke-width="50%" pathLength="100" />
</symbol>
<!--actual pi slices -->
<use class="segment" href="#slice2" stroke="green" stroke-dashoffset="0" stroke-dasharray="30 100" />
<use class="segment" href="#slice2" stroke="orange" stroke-dashoffset="-30" stroke-dasharray="60 100" />
<use class="segment" href="#slice2" stroke="purple" stroke-dashoffset="-90" stroke-dasharray="10 100" />
</svg>
<p>2. Using pathLength="100". <br>Might show a tiny gap on chromium based browsers.</p>
</div>
1. left example: Is using a precice (PI based) circle geometry
The desired circumference of the circle element should be 100 svg units.
Therfore we'll need to set the ideal values like so:
radius: 15.91549430919 (100/2π)
stroke-width: 31.8309886184 (2r)
vieBox width/height: 63.6619772368 (4r)
2. right example: Is using pathLength="100"
PathLength allows us to use any circle dimensions by setting the path's length computation value to "100".
Unfortunately, you might encounter rendering imprecisions on some browsers (e.g. chromium based) resulting in visible gaps between pie segments.
Quite likely, this issue will be fixed in future versions of chromium.
Display pie segments
Eitherway, you can now easily display a percentage based pie segment/slice by setting a stroke dash length value:
Example 30% dash length; offset. 0 (since it's the first segment):
<circle stroke-dashoffset="0" stroke-dasharray="30 100" cx="50%" cy="50%" r="15.9154943092" fill="none" stroke-width="31.8309886184" />
Adding pie segments:
You'll need to decrement (as we need negative values) the dash-offset values progressively by subtracting the previous dash length (percentage):
0, -30, -90
Example: 60% dash length; offset. -30
<circle stroke-dashoffset="-30" stroke-dasharray="60 100" cx="50%" cy="50%" r="15.9154943092" fill="none" stroke-width="31.8309886184" />
Example optimized for reusability (using css variables)
.icon-wrp {
position: relative;
display: inline-block;
width: 200px;
vertical-align: top;
}
.chart {
width: 1em;
height: 1em;
font-size: var(--chartFontSize);
}
.segment {
stroke-dasharray: var(--percent) 100;
stroke-dashoffset: var(--offset);
stroke: var(--strokeColor);
}
.chartAni .segment {
animation-name: progress;
animation-fill-mode: forwards;
animation-delay: 0.3s;
animation-duration: 0.5s;
transition: 0.3s;
stroke-dasharray: 0 100;
}
#keyframes progress {
from {
stroke-dasharray: 0 100;
stroke-dashoffset: 0;
}
to {
stroke-dasharray: var(--percent) 100;
stroke-dashoffset: var(--offset);
}
}
<!-- pie asset – hidden -->
<svg class="svgPieAsset" style="display:none" viewBox="0 0 63.6619772368 63.6619772368">
<symbol id="slice" viewBox="0 0 63.6619772368 63.6619772368">
<circle transform="rotate(-90 31.8309886184 31.8309886184)" id="circle" class="percent" cx="31.8309886184" cy="31.8309886184" r="15.9154943092" fill="none" stroke-width="31.8309886184" />
</symbol>
</svg>
<!-- visible pie chart -->
<div class="icon-wrp">
<svg id="pieChart01" class="chart chartAni" style="--chartFontSize:20vw">
<use class="segment" href="#slice" style="--offset:-0; --percent:33.333; --strokeColor:green" />
<use class="segment" href="#slice" style="--offset:-33.333; --percent:33.333; --strokeColor:purple" />
<use class="segment" href="#slice" style="--offset:-66.666; --percent:33.333; --strokeColor:gray" />
</svg>
</div>
Edit: donut chart example
For a donut chart or a circular gauge – just adjust the stroke-width to your needs.
.icon-wrp {
position: relative;
display: inline-block;
width: 200px;
vertical-align: top;
}
.chart {
width: 1em;
height: 1em;
font-size: var(--chartFontSize);
}
.segment {
stroke-dasharray: var(--percent) 100;
stroke-dashoffset: var(--offset);
stroke: var(--strokeColor);
}
.chartAni .segment {
animation-name: progress;
animation-fill-mode: forwards;
animation-delay: 0.3s;
animation-duration: 0.5s;
transition: 0.3s;
stroke-dasharray: 0 100;
}
#keyframes progress {
from {
stroke-dasharray: 0 100;
stroke-dashoffset: 0;
}
to {
stroke-dasharray: var(--percent) 100;
stroke-dashoffset: var(--offset);
}
}
<!-- pie asset – hidden -->
<svg class="svgPieAsset" style="display:none;" >
<symbol id="slice" viewBox="0 0 33 33">
<circle id="circle" class="percent" cx="50%" cy="50%" r="15.9154943092" fill="none" stroke-width="0.95" />
</symbol>
</svg>
<!-- visible pie chart -->
<div class="icon-wrp">
<svg id="pieChart01" class="chart chartAni" style="--chartFontSize:20vw; transform:rotate(-90deg);">
<use class="segment" href="#slice" style="--offset:0; --percent:10; --strokeColor:blue" />
<use class="segment" href="#slice" style="--offset:-10; --percent:30; --strokeColor:red" />
<use class="segment" href="#slice" style="--offset:-40; --percent:60; --strokeColor:yellow" />
</svg>
</div>
If herrstrietzel's answer doesn't solve your issue for some reason:
Once upon a time I started working on a blog post demonstrating how to generate simple SVG donut/pie charts in React. It's incomplete but it includes all the information you'd need to compute paths for drawing each segment in your chart.
The post itself is React-centric, but the methodology doesn't require React.
The snippet below was generated using the demo in that blog post.
:root {
--color1: #6761a8;
--color2: #009ddc;
--color3: #f26430;
}
svg {
max-width: 180px;
}
path:nth-child(3n + 1) {
fill: var(--color1);
}
path:nth-child(3n + 2) {
fill: var(--color2);
}
path:nth-child(3n + 3) {
fill: var(--color3);
}
<svg viewBox="0 0 100 100">
<path d="M50.99977962889557 22.51817981476399 L50.99993333466665 0.009999666671113516 A50 50 0 1 1 21.909411013411578 91.36325434956197 L34.92449717574351 72.99954813894905 A27.5 27.5 0 1 0 50.99977962889557 22.51817981476399"></path>
<path d="M33.293128455589205 71.84331575559345 L20.27779148719977 90.20684420744341 A50 50 0 0 1 19.110270928347777 10.683023540969941 L32.65908657059322 28.656553196968876 A27.5 27.5 0 0 0 33.293128455589205 71.84331575559345"></path>
<path d="M34.25580929035654 27.45292793069627 L20.707239127704607 9.479213229769087 A50 50 0 0 1 49.000066665333264 0.009999666671113516 L49.00022037110441 22.51817981476399 A27.5 27.5 0 0 0 34.25580929035654 27.45292793069627"></path>
</svg>
Computing the paths
Each <path> represents one segment (slice) of the chart.
To draw the segment you need to compute the coordinates of the 4 corners and connect them with lines and arcs.
Computing the coordinates
Given an angle, a radius, and a center point, you can compute an (x, y) coordinate via this formula:
function getCoordinate(angleInDegrees, radius, center = 50) {
// degrees to radians;
const radians = angleInDegrees * (Math.PI / 180);
const x = center - Math.cos(radians) * radius
const y = center - Math.sin(radians) * radius;
return [x, y];
}
So for a 90° segment with an outer radius of 50 and an inner radius of 20, you can get the corner coordinates via:
const radiusOuter = 50;
const radiusInner = 20;
const angleStart = 0;
const angleEnd = 90;
const [x1, y1] = getCoordinate(angleStart, radiusInner); // starting angle on inner radius
const [x2, y2] = getCoordinate(angleStart, radiusOuter); // starting angle on outer radius
const [x3, y3] = getCoordinate(angleEnd, radiusOuter); // ending angle on outer radius
const [x4, y4] = getCoordinate(angleEnd, radiusInner); // ending angle on inner radius
Connect the coordinates using SVG path commands:
Details about each of the path commands used below can be found at MDN.
const largeArc = 0; // percent > 0.5 ? 1 : 0;
const sweepOuter = 1;
const sweepInner = 0;
const commands = [
// move to start angle coordinate, inner radius (1)
`M${x1} ${y1}`,
// line to start angle coordinate, outer radius (2)
`L${x2} ${y2}`,
// arc to end angle coordinate, outer radius (3)
`A${radiusOuter} ${radiusOuter} 0 ${largeArc} ${sweepOuter} ${x3} ${y3}`,
// line to end angle coordinate, inner radius (4)
`L${x4} ${y4}`,
// arc back to start angle coordinate, inner radius (1)
`A${radiusInner} ${radiusInner} 0 ${largeArc} ${sweepInner} ${x1} ${y1}`
];
Throw it in an SVG and add a little css and you've got your segment:
svg {
width: 250px;
border: 1px solid grey;
}
path {
fill: tomato;
}
<svg viewBox="0 0 100 100">
<path d="
M30 50
L0 50
A50 50 0 0 1 50 0
L50 30
A20 20 0 0 0 30 50
"/>
</svg>
Repeat for the other segments.

CSS animation slowed down/do not complete after adding more sections to page

I am having trouble with CSS animations I added to a Webflow site in an Embed element. Before I added more sections to the page, the animations played a lot faster and the animation fully completed. Now after working on the site and adding more sections, the animations are slow and some do not complete.
Here is the embedded code I am using, if anyone could tell me why this is happening I would greatly appreciate the help :)
I am guessing the code is targeting the body? And that is why the animations got messed up when adding more sections?
<svg xmlns="http://www.w3.org/2000/svg" width="64" height="64" style="height:64px;width:64px">
<path fill="none" stroke="currentColor" stroke-width="2" stroke-miterlimit="10" d="M18 1h28v61L32 48 18 62z" stroke-dasharray="190,192"/>
<path fill="none" stroke="currentColor" stroke-width="2" stroke-linejoin="bevel" stroke-miterlimit="10" d="M23 22l7 7 13-13" stroke-dasharray="29,31"/>
</svg>
<style>
selector {
property: value;
}
svg {
height: 100%;
width: 100%;
}
path {
stroke-dasharray: 300
;
animation: draw 2s normal;
}
#keyframes draw {
from {
stroke-dashoffset: 300
}
to {
stroke-dashoffset: 100%;
}
}
</style>
https://rova-roofing.webflow.io
icon animation section
What you are looking for is animation speed. Try this:
<svg xmlns="http://www.w3.org/2000/svg" width="64" height="64" style="height:64px;width:64px">
<path fill="none" stroke="currentColor" stroke-width="2" stroke-miterlimit="10" d="M18 1h28v61L32 48 18 62z" stroke-dasharray="190,192"/>
<path fill="none" stroke="currentColor" stroke-width="2" stroke-linejoin="bevel" stroke-miterlimit="10" d="M23 22l7 7 13-13" stroke-dasharray="29,31"/>
</svg>
<style>
svg {
height: 100%;
width: 100%;
}
path {
stroke-dasharray: 300px;
;
animation: draw 5s normal; /*Change the speed right here*/
}
#keyframes draw {
from {
stroke-dashoffset: 300px;
}
to {
stroke-dashoffset: 100%;
}
}
</style>

How to center and rotate SVG circle correctly and center polygon in the center of the circle?

I'm trying to let the circles inside rotate. Using 50% in the from and to of the animateTransform didn't work.
The other thing I'm trying is to put the polygon in the center of the circle.
body {
margin: 0;
background: black;
}
#container {
background: rgba(0, 155, 255, 0.3);
padding: 10px;
}
#circle1 {
}
#circle2 {
stroke-dasharray: 20 3 55;
}
#circle3 {
stroke-dasharray: 22;
}
<div id="container">
<svg xmlns="http://www.w3.org/2000/svg" height=207px width=207px stroke="#00bd80" fill="none">
<circle id="circle1" r="100" cx="50%" cy="50%" stroke-width="7px"/>
<circle id="circle2" r="96" cx="50%" cy="50%" stroke-width="5px">
<animateTransform attributeType="xml"
attributeName="transform"
type="rotate"
from="0 98.5 98.5"
to="360 98.5 98.5"
dur="20s"
repeatCount="indefinite"/> <!-- how to center correctly -->
</circle>
<circle id="circle3" r="80" cx="50%" cy="50%" stroke-width="5px"/>
<polygon points="30,15 22.5,28.0 7.5,28.0 0,15 7.5,2.0 22.5,2.0"></polygon>
</svg>
</div>
Your canvas is 207px wide and 207px high. The circles are centred at 50% so at 207/2 = 103.5px so that's where you need to do your rotation around.
The polygon is 30px wide and 26px across so you can just translate that into place too. You could combine the two polygon translate commands, I've left them separate so it's clearer what I've done.
console.log(document.getElementsByTagName("polygon")[0].getBBox())
body {
margin: 0;
background: black;
}
#container {
background: rgba(0, 155, 255, 0.3);
padding: 10px;
}
#circle1 {
}
#circle2 {
stroke-dasharray: 20 3 55;
}
#circle3 {
stroke-dasharray: 22;
}
<div id="container">
<svg xmlns="http://www.w3.org/2000/svg" height=207px width=207px stroke="#00bd80" fill="none">
<circle id="circle1" r="100" cx="50%" cy="50%" stroke-width="7px"/>
<circle id="circle2" r="96" cx="50%" cy="50%" stroke-width="5px">
<animateTransform attributeType="xml"
attributeName="transform"
type="rotate"
from="0 103.5 103.5"
to="360 103.5 103.5"
dur="20s"
repeatCount="indefinite"/> <!-- how to center correctly -->
</circle>
<circle id="circle3" r="80" cx="50%" cy="50%" stroke-width="5px"/>
<polygon transform="translate(103.5, 103.5) translate(-15, -13)" points="30,15 22.5,28.0 7.5,28.0 0,15 7.5,2.0 22.5,2.0"></polygon>
</svg>
</div>

Add shadow to an SVG element, such as line

So I'm pretty new to SVG but I started playing with a graph. The graph is from here. I've been searching for hours and did only find a half solution to my problem. As you see from the code snippet, if you hover on the graph it "glows". But I want that only the circles and the "joints" would glow when I'm hovering on them.
What I tried:
Using regular CSS shadowing
Using the code that makes "glow" the graph, but only on g elements.
Creating a separate SVG both: a) in the main SVG b) separately, and
mixing them with position: absolute. (after this the positioning
worked weirdly)
What should I do to make only the circles and the joints "glow"?
*{
box-sizing: border-box;
margin: 0;
padding: 0;
}
html, body{
height: 100%;
width: 100%;
}
#import url('https://fonts.googleapis.com/css?family=Roboto+Mono:400,500&display=swap');
body {
font-family: 'Roboto Mono', monospace;
}
.graph .labels.x-labels {
text-anchor: middle;
}
.graph .labels.y-labels {
text-anchor: end;
}
.graph {
height: 500px;
width: 800px;
}
.graph .grid {
stroke: #ccc;
stroke-dasharray: 0;
stroke-width: 3;
}
.labels {
font-size: 17px;
font-weight: 400;
}
.label-title {
font-weight: 500;
text-transform: uppercase;
font-size: 15px;
fill: black;
}
.data {
fill: #f86d36;
stroke-width: 1;
}
.graph .dot-joints {
stroke: #f86d36;
stroke-dasharray: 0;
stroke-width: 3;
}
svg:hover {
-webkit-filter: drop-shadow(0px 0px 4px #f86d36e8);
filter: drop-shadow(0px 0px 4px #f86d36e8);
}
<svg version="1.2" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" class="graph"
aria-labelledby="title" role="img">
<g class="grid x-grid" id="xGrid">
<line x1="90" x2="90" y1="5" y2="371"></line>
</g>
<g class="grid x-grid" id="xGrid">
<line x1="90" x2="705" y1="370" y2="370"></line>
</g>
<g class="labels x-labels"><text x="100" y="400">2008</text><text x="246" y="400">2009</text><text x="392"
y="400">2010</text><text x="538" y="400">2011</text><text x="694" y="400">2012</text><text x="400" y="440"
class="label-title">Year</text></g>
<g class="labels x-labels"><text x="70" y="15">15</text><text x="70" y="131">10</text><text x="70"
y="248">5</text><text x="70" y="373">0</text><text x="35" y="200" class="label-title">Price</text></g>
<g class="data" data-setname="Our first data set">
<circle cx="95" cy="192" data-value="7.2" r="5"></circle>
<circle cx="240" cy="141" data-value="8.1" r="5"></circle>
<circle cx="388" cy="179" data-value="7.7" r="5"></circle>
<circle cx="531" cy="200" data-value="6.8" r="5"></circle>
<circle cx="677" cy="104" data-value="6.7" r="5"></circle>
</g>
<g class="dot-joints x-grid">
<line x1="95" x2="240" y1="192" y2="141"></line>
<line x1="240" x2="388" y1="141" y2="179"></line>
<line x1="388" x2="531" y1="179" y2="200"></line>
<line x1="531" x2="677" y1="200" y2="104"></line>
</g>
</svg>
I hope this is what you need: I'm using an svg filter for the shadow.
To your code I've added .data circle:hover{filter:url(#f)}for the individual circles and .dot-joints.x-grid:hover{filter:url(#f)} for the group of lines:
*{
box-sizing: border-box;
margin: 0;
padding: 0;
}
html, body{
height: 100%;
width: 100%;
}
#import url('https://fonts.googleapis.com/css?family=Roboto+Mono:400,500&display=swap');
body {
font-family: 'Roboto Mono', monospace;
}
.graph .labels.x-labels {
text-anchor: middle;
}
.graph .labels.y-labels {
text-anchor: end;
}
.graph {
height: 500px;
width: 800px;
}
.graph .grid {
stroke: #ccc;
stroke-dasharray: 0;
stroke-width: 3;
}
.labels {
font-size: 17px;
font-weight: 400;
}
.label-title {
font-weight: 500;
text-transform: uppercase;
font-size: 15px;
fill: black;
}
.data {
fill: #f86d36;
stroke-width: 1;
}
.data circle:hover{filter:url(#f)}
.graph .dot-joints {
stroke: #f86d36;
stroke-dasharray: 0;
stroke-width: 3;
}
.dot-joints.x-grid:hover{filter:url(#f)}
<svg version="1.2" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" class="graph"
aria-labelledby="title" role="img">
<defs>
<filter id="f" filterUnits="userSpaceOnUse" id="shadow" x="-10" y="-150" width="120%" height="120%">
<feGaussianBlur in="SourceAlpha" stdDeviation="5" result="blur"></feGaussianBlur>
<feOffset in="blur" dx="3" dy="1" result="shadow"></feOffset>
<feFlood flood-color="rgba(0,0,0,.52)" result="color" />
<feComposite in ="color" in2="shadow" operator="in" />
<feComposite in="SourceGraphic"/>
</filter>
</defs>
<g class="grid x-grid" id="xGrid">
<line x1="90" x2="90" y1="5" y2="371"></line>
</g>
<g class="grid x-grid" id="xGrid">
<line x1="90" x2="705" y1="370" y2="370"></line>
</g>
<g class="labels x-labels"><text x="100" y="400">2008</text><text x="246" y="400">2009</text><text x="392"
y="400">2010</text><text x="538" y="400">2011</text><text x="694" y="400">2012</text><text x="400" y="440"
class="label-title">Year</text></g>
<g class="labels x-labels"><text x="70" y="15">15</text><text x="70" y="131">10</text><text x="70"
y="248">5</text><text x="70" y="373">0</text><text x="35" y="200" class="label-title">Price</text></g>
<g class="data" data-setname="Our first data set">
<circle cx="95" cy="192" data-value="7.2" r="5"></circle>
<circle cx="240" cy="141" data-value="8.1" r="5"></circle>
<circle cx="388" cy="179" data-value="7.7" r="5"></circle>
<circle cx="531" cy="200" data-value="6.8" r="5"></circle>
<circle cx="677" cy="104" data-value="6.7" r="5"></circle>
</g>
<g class="dot-joints x-grid" >
<line x1="95" x2="240" y1="192" y2="141"></line>
<line x1="240" x2="388" y1="141" y2="179"></line>
<line x1="388" x2="531" y1="179" y2="200"></line>
<line x1="531" x2="677" y1="200" y2="104"></line>
</g>
</svg>
Alternatively you may want this instead:
svg:hover .data,
svg:hover .dot-joints.x-grid{filter:url(#f)}
When hovering the svg element apply shadow to the lines and circles.
*{
box-sizing: border-box;
margin: 0;
padding: 0;
}
html, body{
height: 100%;
width: 100%;
}
#import url('https://fonts.googleapis.com/css?family=Roboto+Mono:400,500&display=swap');
body {
font-family: 'Roboto Mono', monospace;
}
.graph .labels.x-labels {
text-anchor: middle;
}
.graph .labels.y-labels {
text-anchor: end;
}
.graph {
height: 500px;
width: 800px;
}
.graph .grid {
stroke: #ccc;
stroke-dasharray: 0;
stroke-width: 3;
}
.labels {
font-size: 17px;
font-weight: 400;
}
.label-title {
font-weight: 500;
text-transform: uppercase;
font-size: 15px;
fill: black;
}
.data {
fill: #f86d36;
stroke-width: 1;
}
.graph .dot-joints {
stroke: #f86d36;
stroke-dasharray: 0;
stroke-width: 3;
}
svg:hover .data,
svg:hover .dot-joints.x-grid{filter:url(#f)}
<svg version="1.2" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" class="graph"
aria-labelledby="title" role="img">
<defs>
<filter id="f" filterUnits="userSpaceOnUse" id="shadow" x="-10" y="-150" width="120%" height="120%">
<feGaussianBlur in="SourceAlpha" stdDeviation="5" result="blur"></feGaussianBlur>
<feOffset in="blur" dx="3" dy="1" result="shadow"></feOffset>
<feFlood flood-color="rgba(0,0,0,.52)" result="color" />
<feComposite in ="color" in2="shadow" operator="in" />
<feComposite in="SourceGraphic"/>
</filter>
</defs>
<g class="grid x-grid" id="xGrid">
<line x1="90" x2="90" y1="5" y2="371"></line>
</g>
<g class="grid x-grid" id="xGrid">
<line x1="90" x2="705" y1="370" y2="370"></line>
</g>
<g class="labels x-labels"><text x="100" y="400">2008</text><text x="246" y="400">2009</text><text x="392"
y="400">2010</text><text x="538" y="400">2011</text><text x="694" y="400">2012</text><text x="400" y="440"
class="label-title">Year</text></g>
<g class="labels x-labels"><text x="70" y="15">15</text><text x="70" y="131">10</text><text x="70"
y="248">5</text><text x="70" y="373">0</text><text x="35" y="200" class="label-title">Price</text></g>
<g class="data" data-setname="Our first data set">
<circle cx="95" cy="192" data-value="7.2" r="5"></circle>
<circle cx="240" cy="141" data-value="8.1" r="5"></circle>
<circle cx="388" cy="179" data-value="7.7" r="5"></circle>
<circle cx="531" cy="200" data-value="6.8" r="5"></circle>
<circle cx="677" cy="104" data-value="6.7" r="5"></circle>
</g>
<g class="dot-joints x-grid" >
<line x1="95" x2="240" y1="192" y2="141"></line>
<line x1="240" x2="388" y1="141" y2="179"></line>
<line x1="388" x2="531" y1="179" y2="200"></line>
<line x1="531" x2="677" y1="200" y2="104"></line>
</g>
</svg>
UPDATE
The OP is commenting:
Can you explain it in a little more details
In the <defs> I've added an svg filter. This filter first is creating a blur feGaussianBlur. You may need to change the stdDeviation in order to change the aspect of the shadow.
Next is offsetting the the previously created blur feOffset: You may need to change the dx="3"and dy="1" attributes in order to move the shadow.
Then feFlood and feComposite are used to add a color to the shadow. In this case I'm using a semitransparent black, but you can use the color you want.
I'm using this filter to apply the shadow only to those elements you want: filter:url(#f) - where f is the filter's id