css/html- to create a circle with a hole in the bottom - svg

enter image description here
I wanna make circle chart like this.
below is my code.
If I invert up and down in that code, i get the shape you want.
But what I really want is a graph with the yellow graph pointing further to the right. How do I adjust the spacing?
Please help me
<svg width="4.5rem" height="4.5rem" viewBox="0 0 40 40">
<circle class="pieSegment segment1" cx="20" cy="20" r="15.915494309189533" fill="transparent" stroke="#1a0d1c" stroke-dashoffset="11" stroke-dasharray="74" stroke-width="5"></circle>
<circle class="pieSegment segment3" cx="20" cy="20" r="15.915494309189533" fill="transparent" stroke="#e9ad61" stroke-dashoffset="14" stroke-dasharray="25 75" stroke-width="5"></circle>
</svg>

Edit (missed the previous answer by #jeremy-denis also using pathLength)
Here is another approach also using pathLength value:
See also MDN Docs: pathLength
example horse shoe gauge
let progress = document.querySelector("#progress");
let svgGauges = document.querySelectorAll(".svgGauge");
let gauges = document.querySelectorAll(".gaugePercent");
let percentText = document.querySelector(".percentText");
progress.addEventListener("change", function(e) {
let percent = e.target.value;
if (svgGauges.length) {
svgGauges.forEach(function(item, i) {
let currentGauge = svgGauges[i];
let gauge = currentGauge.querySelector(".gaugePercent");
let dashGap = gauge.getAttribute("stroke-dasharray").split(" ")[1];
// console.log(gauge);
gauge.setAttribute("stroke-dasharray", percent + " " + dashGap);
gauge.setAttribute("style", "animation-fill-mode: none");
percentText.textContent = percent;
});
}
});
.gaugeWrap {
display: inline-block;
width: 10rem;
}
.layout {
width: 50%;
margin: 0 auto;
display: flex;
justify-content: center;
}
.txt-cnt {
text-align: center;
}
.circle {
transition: 0.3s;
stroke-width: 15;
}
.linecap-round .circle {
stroke-linecap: round;
shape-rendering: geometricPrecision;
}
.linecap-round .circle[stroke-dasharray^="0 "] {
stroke-linecap: unset;
}
<p class="txt-cnt">Progress (<span class="percentText">50</span> %) <input id="progress" style="width:100px" type="range" min="0" max="100" step="10" value="50" />
</p>
<div class="layout">
<div class="gaugeWrap">
<p>horse shoe gauge 270°</p>
<svg class="svgGauge linecap-round" viewBox="0 0 120 120" xmlns="http://www.w3.org/2000/svg" transform="scale(-1 1) rotate(-225) ">
<circle class="circle gaugeBG" id="gaugeBG" cx="50%" cy="50%" r="45" pathLength="133.333" fill="none" stroke="#000" stroke-dasharray="100 100" />
<circle class="circle gaugePercent " id="gauge" cx="50%" cy="50%" r="45" pathLength="133.333" fill="none" stroke-dasharray="50 133.333" stroke="#e9ad61" />
</svg>
</div>
<div class="gaugeWrap">
<p>Semi-circle gauge 180°</p>
<svg class="svgGauge linecap-round" viewBox="0 0 120 120" xmlns="http://www.w3.org/2000/svg" transform="scale(-1 1) rotate(-180)">
<circle class="circle gaugeBG " id="gaugeBG" cx="50%" cy="50%" r="45" pathLength="200" fill="none" stroke="#000" stroke-dasharray="100 200" />
<circle class="circle gaugePercent" id="gauge" cx="50%" cy="50%" r="45" pathLength="200" fill="none" stroke-dasharray="50 200" stroke="#e9ad61" />
</svg>
</div>
<div class="gaugeWrap">
<p>Full-circle gauge 360°</p>
<svg class="svgGauge" viewBox="0 0 120 120" xmlns="http://www.w3.org/2000/svg" transform="rotate(-90)">
<circle class="circle gaugeBG" id="gaugeBG" cx="50%" cy="50%" r="45" pathLength="100" fill="none" stroke="#000" stroke-dasharray="100 100" />
<circle class="circle gaugePercent" id="gauge" cx="50%" cy="50%" r="45" pathLength="100" fill="none" stroke-linecap="butt" stroke-dasharray="50 100" stroke="#e9ad61" />
</svg>
</div>
</div>
Your example has approximately a 270° range (3/4 of 360°).
By setting pathLengthto 133 (360/270*100) we change the computation of the dash lengths.
Since we don't use the complete circumference of the circle (see the full circle example with pathLength=100) we increase the pathlength to make this 3/4 segment to have a length of 100 units.
We can set the first stroke-dasharray value e.g. to 50 (display 50% progress).
The 2nd value 133 ensures the dash gap is large enough to fill the rest of the circle gauge.
To make the horse shoe gauge stand upright we need to calculate a rotation like this:
(360 + 270)/2 - 90 = -225
So we can rotate the parent svg by setting the transform attribute to
transform="scale(-1 1) rotate(-225)"
Gauge generator pen

an idea can be to use pathLength="100" to your circle
that allow you to give a percent in the attribute stroke-dashoffset with 100% - {percent of circle to fill}
then we can imagine rotate the circle to the correct shape
#mySvg {
transform: rotate(-125deg) scaleX(-1);
}
to round the border of your line you can use
stroke-linecap: round;
#mySvg {
transform: rotate(-125deg) scaleX(-1);
}
#mySvg circle {
stroke-linecap: round;
shape-rendering: geometricPrecision;
}
<svg id="mySvg" width="4.5rem" height="4.5rem" viewBox="0 0 40 40">
<circle class="pieSegment segment1" cx="20" cy="20" r="15.915494309189533" fill="transparent" stroke="#1a0d1c" stroke-dashoffset="20" stroke-dasharray="100" stroke-width="5" pathLength="100"></circle>
<circle class="pieSegment segment3" cx="20" cy="20" r="15.915494309189533" fill="transparent" stroke="#e9ad61" stroke-dashoffset="80" stroke-dasharray="100" stroke-width="5" pathLength="100"></circle>
</svg>

Related

How to make a circular bullet point containing a number where the number text must scale

So I have been asked to make a section number that is constructed as a non-filled circle with a 2px border containing a number. The range of the numbers is 1 to 999.
As you can see from the example below, it looks pleasing at one & two digit section numbers, but when we hit three digits the number is clipped.
My thinking is that there needs to be a process where the text is drawn, measured, then scaled to fit into the target space inside the circle, where the target space is effectively a square rect 60% of the diameter of the circle.
However, no JS is allowed in the solution.
I thought it might be possible using SVG and its scaling capabilities via the 'preserveAspectRatio' parameter. However the image above is a screen grab of my SVG results. Working snippet below.
My intention with the code was to have the inner SVG containing the text resize proportionally so that it would fit the width of the parent, with the height set to auto so that it would change in proportion.
Can anyone tell me where I am going wrong?
PS. I am using Chrome on PC to test.
Note: In the following snippet the output looks like the image, so 8, then 88, then 888. The markup is the same for each case - only the text content changes.
body {
padding: 20px;
overflow: hidden;
background-color: #f0f0f0;
}
.counterDiv {
width: 44px;
max-width: 44px;
min-width: 44px;
height: 44px;
max-height: 44px;
min-height: 44px;
margin: 0;
padding: 0;
}
svg {
width: 100%;
height: 100%;
}
text {
font: normal normal 18pt Helvetica, Arial, Verdana;
}
<div class='counterDiv' style='position: relative;'>
<svg viewbox="0 0 44 44">
<g>
<circle cx="22" cy="22" r="20" stroke="#cd1041" stroke-width="2px" fill-opacity="0" />
<svg viewBox="0 0 100 auto" x="20%" width="60%" preserveAspectRatio="xMidYMid meet">
<text x="50%" y="50%" alignment-baseline="middle" text-anchor="middle" fill="#cd1041" dy=".1em">8</text>
</svg>
</g>
</svg>
</div>
<div class='counterDiv' style='position: relative;'>
<svg viewbox="0 0 44 44">
<g>
<circle cx="22" cy="22" r="20" stroke="#cd1041" stroke-width="2px" fill-opacity="0" />
<svg viewBox="0 0 100 auto" x="20%" width="60%" preserveAspectRatio="xMidYMid meet">
<text x="50%" y="50%" alignment-baseline="middle" text-anchor="middle" fill="#cd1041" dy=".1em">88</text>
</svg>
</g>
</svg>
</div>
<div class='counterDiv' style='position: relative;'>
<svg viewbox="0 0 44 44">
<g>
<circle cx="22" cy="22" r="20" stroke="#cd1041" stroke-width="2px" fill-opacity="0" />
<svg viewBox="0 0 100 auto" x="20%" width="60%" preserveAspectRatio="xMidYMid meet">
<text x="50%" y="50%" alignment-baseline="middle" text-anchor="middle" fill="#cd1041" dy=".1em">888</text>
</svg>
</g>
</svg>
</div>
A posible solution would be using textLength and lengthAdjust. The lengthAdjust attribute controls how the text is stretched into the length defined by the textLength attribute. One inconvinient would be that the 1 digit numbers would be stretched.
An alternative solution would be using a smaller font size.
Also you may want to use javascript to target only the 3 digit text elements.
body {
padding: 20px;
overflow: hidden;
background-color: #f0f0f0;
}
.counterDiv {
width: 44px;
max-width: 44px;
min-width: 44px;
height: 44px;
max-height: 44px;
min-height: 44px;
margin: 0;
padding: 0;
}
svg {
width: 100%;
height: 100%;
}
text {
font: normal normal 18pt Helvetica, Arial, Verdana;
}
<div class='counterDiv' style='position: relative;'>
<svg viewbox="0 0 44 44">
<g>
<circle cx="22" cy="22" r="20" stroke="#cd1041" stroke-width="2px" fill-opacity="0" />
<svg viewBox="0 0 100 auto" x="20%" width="60%" preserveAspectRatio="xMidYMid meet">
<text x="50%" y="50%" alignment-baseline="middle" text-anchor="middle" fill="#cd1041" dy=".1em" textLength="25" lengthAdjust="spacingAndGlyphs">8</text>
</svg>
</g>
</svg>
</div>
<div class='counterDiv' style='position: relative;'>
<svg viewbox="0 0 44 44">
<g>
<circle cx="22" cy="22" r="20" stroke="#cd1041" stroke-width="2px" fill-opacity="0" />
<svg viewBox="0 0 100 auto" x="20%" width="60%" preserveAspectRatio="xMidYMid meet">
<text x="50%" y="50%" alignment-baseline="middle" text-anchor="middle" fill="#cd1041" dy=".1em" textLength="25" lengthAdjust="spacingAndGlyphs">88</text>
</svg>
</g>
</svg>
</div>
<div class='counterDiv' style='position: relative;'>
<svg viewbox="0 0 44 44">
<g>
<circle cx="22" cy="22" r="20" stroke="#cd1041" stroke-width="2px" fill-opacity="0" />
<svg viewBox="0 0 100 auto" x="20%" width="60%" preserveAspectRatio="xMidYMid meet">
<text x="50%" y="50%" alignment-baseline="middle" text-anchor="middle" fill="#cd1041" dy=".1em" textLength="25" lengthAdjust="spacingAndGlyphs">888</text>
</svg>
</g>
</svg>
</div>
Not for OP,
but for all those SVG loving people who have an inner-guide (or sensible boss)
that tells them: "JavaScript is fine, when applied with common sense"
customElements.define("svg-counter", class extends HTMLElement {
connectedCallback() {
this.render();
}
render(
val = this.getAttribute("value") || "888",
color = "green",
circlestrokewidth = 4,
circlestroke = "red",
circlefill = "none"
){
let id = "P" + (new Date() / 1); // uniqueid
let singleDigit = val.length == 1;
this.innerHTML = `
<svg viewbox="0 0 100 100">
<circle cx="50" cy="50" r="${50-circlestrokewidth}" stroke="${circlestroke}"
fill="${circlefill}" stroke-width="${circlestrokewidth}"/>
<path id="${id}" pathLength="100" d="M0 60H100" stroke="none"/>
<text transform="scale(${singleDigit?1:.7})" transform-origin="50 50">
<textPath href="#${id}" method="stretch"
textlength="100" lengthAdjust="${singleDigit?"":"spacingAndGlyphs"}"
startoffset="50" text-anchor="middle" dominant-baseline="middle"
font-family="Helvetica"
fill="${color}" font-size="${100}px">${val}</textPath>
</text>
</svg>`;
}
});
svg {
width: 20%;
}
<svg-counter value="8"></svg-counter>
<svg-counter value="88"></svg-counter>
<svg-counter value="888"></svg-counter>
<svg-counter value="8888"></svg-counter>
Alas FireFox has some issues, slight tweaked version to make it work in FireFox:
Alas I have a boss who says "We don't care about FireFox customers"
customElements.define("svg-counter", class extends HTMLElement {
connectedCallback() {
this.render();
}
render(
val = this.getAttribute("value") || "888",
color = "green",
circlestrokewidth = 4,
circlestroke = "red",
circlefill = "none"
){
let id = "P" + (new Date() / 1) + val.length; // uniqueid
let singleDigit = val.length == 1;
this.innerHTML = `
<svg viewbox="0 0 100 100">
<circle cx="50" cy="50" r="${50-circlestrokewidth}" stroke="${circlestroke}"
fill="${circlefill}" stroke-width="${circlestrokewidth}"/>
<path id="${id}" pathLength="100" d="M0 55H100" stroke="blue"/>
<text transform="scale(${[0,1.7,1.2,.9,.7][val.length]})" transform-origin="50 50">
<textPath href="#${id}" method="stretch"
textlength="100" lengthAdjust="${singleDigit?"":"spacingAndGlyphs"}"
startoffset="50" text-anchor="middle" dominant-baseline="middle"
font-family="Helvetica"
fill="${color}" font-size="50px">${val}</textPath>
</text>
</svg>`;
}
});
svg {
width: 20%;
}
<svg-counter value="8"></svg-counter>
<svg-counter value="88"></svg-counter>
<svg-counter value="888"></svg-counter>
<svg-counter value="8888"></svg-counter>

How to create an SVG icon consisting of two overlapping shapes, one with a hole?

I have made a progress bar icon from two partially overlapping SVG shapes.
I would like the icon to have the same color as the surrounding text, so I set stroke and fill to currentColor.
The icon is displayed correctly if the color of the surrounding text doesn't have alpha channel e.g. color: black. However, if the color of the surrounding text has alpha channel e.g. color: rgba (0, 0, 0, 0.7), then the icon is darker where the shapes overlap.
How can I get the same color at each point of the icon?
body { background-color: #eee; color: rgba(0, 0, 0, 0.7); }
<svg viewBox="0 0 44 18" width="220" height="90">
<rect x="2" y="2" width="40" height="11" rx="3" ry="3"
stroke="currentColor" stroke-width="2" fill="transparent" />
<rect x="2" y="2.5" width="24" height="10" fill="currentColor" />
</svg>
Question Prevent overlapping figures with alpha channel from shading each other? is very similar, but the accepted answer doesn't work in this case:
body { background-color: #eee; color: rgba(0, 0, 0, 0.7); }
<svg viewBox="0 0 44 18" width="220" height="90">
<defs>
<clipPath id="myClip">
<rect x="2" y="2" width="40" height="11" rx="3" ry="3"
stroke="black" stroke-width="2" fill="transparent" />
<rect x="2" y="2.5" width="24" height="10" />
</clipPath>
</defs>
<rect width="100%" height="100%" fill="currentColor" clip-path="url(#myClip)"/>
</svg>
Update:
I decided to draw the icon without overlapping parts, because it is much easier and it also looks good:
body { background-color: #eee; color: rgba(0, 0, 0, 0.7); }
<svg viewBox="0 0 44 18" width="220" height="90">
<rect x="2" y="2" width="40" height="11" rx="3" ry="3"
stroke="currentColor" stroke-width="2" fill="transparent" />
<rect x="4" y="4" width="23" height="7" rx="2" ry="2" fill="currentColor" />
</svg>
One solution is to user a mask. But if you don't change the geometry, you will get the same issue as with #InvisibleGorilla's solution: you will get antialiasing artifacts.
body { background-color: #eee; color: rgba(0, 0, 0, 0.7); }
<svg viewBox="0 0 44 18" width="220" height="90">
<defs>
<mask id="cutout">
<rect width="100%" height="100%" fill="white" />
<use xlink:href="#bar" fill="black" />
</mask>
<rect id="bar" x="2" y="2.5" width="24" height="10" />
</defs>
<rect x="2" y="2" width="40" height="11" rx="3" ry="3"
stroke="currentColor" stroke-width="2" fill="transparent"
mask="url(#cutout)" />
<use xlink:href="#bar" fill="currentColor" />
</svg>
To fix the antialiasing artifacts, move your bar so that it is positioned at a whole pixel (y="2") instead of a half pixel (y="2.5"). You still might still see very slight artifacts at some scales. But it should be a lot better.
body { background-color: #eee; color: rgba(0, 0, 0, 0.7); }
<svg viewBox="0 0 44 18" width="220" height="90">
<defs>
<mask id="cutout">
<rect width="100%" height="100%" fill="white" />
<use xlink:href="#bar" fill="black" />
</mask>
<rect id="bar" x="2" y="2" width="24" height="10" />
</defs>
<rect x="2" y="2" width="40" height="11" rx="3" ry="3"
stroke="currentColor" stroke-width="2" fill="transparent"
mask="url(#cutout)" />
<use xlink:href="#bar" fill="currentColor" />
</svg>
Before the rect where fill has currentColor, add a rect with the exact same attributes but with a white fill directly before it (so that it gets drawn underneath it in the svg).
See snipped below:
<style>
body { background-color: #eee; color: rgba(0, 0, 0, 0.7); }
</style>
<svg viewBox="0 0 44 18" width="220" height="90">
<rect x="2" y="2" width="40" height="11" rx="3" ry="3"
stroke="currentColor" stroke-width="2" fill="transparent" />
<rect x="2" y="2.5" width="24" height="10" fill="white" />
<rect x="2" y="2.5" width="24" height="10" fill="currentColor" />
</svg>
You could also change the order of your rects and add a matching white rect under the one where the stroke has currentColor.
Here's the updated snippet using background color and underlying both rects. I've tested on Chrome and Firefox on Mac and I don't see any lines from the background color rects.
<style>
body { background-color: #232b32; color: rgba(240, 240, 240, 0.7); }
</style>
<svg viewBox="0 0 44 18" width="220" height="90">
<rect x="2" y="2" width="40" height="11" rx="3" ry="3"
stroke="#232b32" stroke-width="2" fill="transparent" />
<rect x="2" y="2" width="40" height="11" rx="3" ry="3"
stroke="currentColor" stroke-width="2" fill="transparent" />
<rect x="2" y="2.5" width="24" height="10" fill="#232b32" />
<rect x="2" y="2.5" width="24" height="10" fill="currentColor" />
</svg>
<p>Some text</p>
Here's a screenshot of when I run this snippet.

fixed width and height of svg objects

I want to keep my polygon circle and path width same width and height on all devices for example 50px height and 50px width, because they are streched on mobile devices, or if there is another solution.
i deleted some css classes and other svg objects so code can be seen as simple.
<svg preserveAspectRatio="none" id="Layer_1" data-name="Layer 1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" viewBox="0 0 734 414">
<g id="bg">
<rect class="cls-1" x="-0.01" y="0.96" width="733.99" height="92.47" />
<g id="bg_elements">
<path class="cls-2" d="M734,414H0V81.84s56,2.53,74.56,2.73c22,.23,64.67-3,86.71-2.86,24.83.18,109.22,6.38,136.17,7.46,45.2,1.8,105-5.57,149.73-7.27,39.45-1.51,118.14,3.66,157.5,3.66,32.42,0,129.33-5.29,129.33-5.29Z" />
<g id="elements">
<polygon id="triangle" class="cls-3" points="32.27 247.14 23.13 224.36 7.97 243.67 32.27 247.14" />
<circle id="ellipse" class="cls-4" cx="317.06" cy="160.33" r="12.53" />
<polygon id="triangle_s" class="cls-5" points="460.08 371.37 448.65 376.05 438.89 383.61 440.56 371.37 438.89 359.14 448.65 366.7 460.08 371.37" />
<path id="plus" class="cls-6" d="M651.11,227l-13.62,21.69m-4-
17.66,21.7,13.63" />
</g>
</g>
</g>
</svg>
If the svg has the viewBox and preserveAspectRatio all set, another place possibility is to use a container div and font-size something like:
<div style="height: 50px; width: 50px; fontSize: 50px">
<svg>...</svg>
</div>
Your requirements are not clear. But perhaps what you want, is for the SVG to cover the whole of the viewport whilst still staying unstretched/unsquashed. Ie. the equivalent of background-size: cover for HTML elements.
If that is the case, then what you want is:
preserveAspectRatio="xMidYMid slice"
This will size the SVG up or down so that it is the minimum size that still entirely covers the whole of the viewport. However you may end up with some parts of the SVG getting clipped off the edges.
svg {
width: 400px;
height: 300px;
}
path {
fill: red;
}
circle {
fill: blue;
}
polygon {
fill: yellow;
}
<svg preserveAspectRatio="xMidYMid slice" viewBox="0 0 734 414">
<g id="bg">
<rect class="cls-1" x="-0.01" y="0.96" width="733.99" height="92.47" />
<g id="bg_elements">
<path class="cls-2" d="M734,414H0V81.84s56,2.53,74.56,2.73c22,.23,64.67-3,86.71-2.86,24.83.18,109.22,6.38,136.17,7.46,45.2,1.8,105-5.57,149.73-7.27,39.45-1.51,118.14,3.66,157.5,3.66,32.42,0,129.33-5.29,129.33-5.29Z" />
<g id="elements">
<polygon id="triangle" class="cls-3" points="32.27 247.14 23.13 224.36 7.97 243.67 32.27 247.14" />
<circle id="ellipse" class="cls-4" cx="317.06" cy="160.33" r="12.53" />
<polygon id="triangle_s" class="cls-5" points="460.08 371.37 448.65 376.05 438.89 383.61 440.56 371.37 438.89 359.14 448.65 366.7 460.08 371.37" />
<path id="plus" class="cls-6" d="M651.11,227l-13.62,21.69m-4-
17.66,21.7,13.63" />
</g>
</g>
</g>
</svg>

Issue when zooming in on SVG donut chart in Safari

I'm attempting to create a donut chart using SVG, and am running into a problem when viewing it in Safari. Here's a fiddle that shows the issue; I'll describe it in detail below:
https://jsfiddle.net/nijhazer/phy2ossh/
This fiddle shows a graphic comprised of two circles overlaid atop one another. The problem becomes apparent when a Safari user increases zoom size in her browser:
Relevant HTML from the example:
<div class="donut-chart">
<svg width="200" height="200">
<circle class="backdrop" cx="100" cy="100" r="65" fill="#d5d8d5" stroke="none" stroke-width="0"></circle>
<circle class="progress" cx="100" cy="100" r="75" fill="none" stroke="lightgreen" stroke-width="20" style="stroke-dashoffset: 353.428875px;"></circle>
<circle class="outer-ring" cx="100" cy="100" r="85" fill="none" stroke="#d5d8d5" stroke-width="1"></circle>
</svg>
</div>
Relevant CSS from the example:
body {
background-color: white;
}
.donut-chart {
width: 200px;
height: 200px;
position: relative;
}
svg {
width: 200px;
height: 200px;
}
.progress {
stroke-dasharray: 471.24;
transform: rotate(-90deg);
transform-origin: 50% 50%;
background-color: transparent;
}
I don't know if this helps, but at I can read from the comments that people are blaming the CSS property transform-origin. In this example I moved the styling to attributes on the circle element. I also added pathLength to make it easier to control the progress bar.
body {
background-color: white;
}
.donut-chart {
width: 200px;
height: 200px;
position: relative;
}
svg {
width: 200px;
height: 200px;
}
<div class="donut-chart">
<svg viewBox="0 0 200 200" xmlns="http//www.w3.org/2000/svg">
<circle class="backdrop" cx="100" cy="100" r="65" fill="#d5d8d5"
stroke="none" stroke-width="0" />
<circle class="progress" cx="100" cy="100" r="75" fill="none"
stroke="lightgreen" stroke-width="20" stroke-dasharray="25 100"
transform="rotate(-90 100 100)" pathLength="100" />
<circle class="outer-ring" cx="100" cy="100" r="85" fill="none"
stroke="#d5d8d5" stroke-width="1" />
</svg>
</div>
I can confirm chrwahl's approach is working (at least in MacOS/IOS safari versions (15.4) I've tested - Unfortunately, the support of functions may vary from version to version)
Another workaround could be to add a translateX offset before rotating like so:
.donut-chart {
width: 200px;
height: 200px;
position: relative;
display: block;
}
svg {
width: 200px;
height: 200px;
}
.progress {
stroke-dasharray: 471.24;
background-color: transparent;
transform: translate(0px, 200px) rotate(-90deg);
}
<div class="donut-chart">
<svg width="200" height="200" viewBox="0 0 200 200">
<circle class="backdrop" cx="50%" cy="50%" r="65" fill="#d5d8d5" stroke="none" stroke-width="0"></circle>
<circle class="progress" cx="50%" cy="50%" r="75" fill="none" stroke="lightgreen" stroke-width="20" style="stroke-dashoffset: 353.428875px;"></circle>
<circle class="outer-ring" cx="100" cy="100" r="85" fill="none" stroke="#d5d8d5" stroke-width="1"></circle>
</svg>
</div>
This "hack" won't work combined with transform-origin: 50% 50%

SVG moves out of position when screen size changes or doesn't scale

I've created an SVG and put the width as a percentage because I want it to resize to fit different screen widths, but when I resize the screen, the svg moves up and down and doesn't move left/right to stay in the centre. If I use pixels instead of percentages, it doesn't resize with the screen.
Preview didn't work on here so here's the codepen link
.
HTML
<svg height="100%" width="100%" id="main">
<circle class="graph line line-1" cx="50%" cy="50%" r="25%" stroke-width="5%" stroke="#f1c40f" fill="none" />
<circle class="graph line line-2" cx="50%" cy="50%" r="20%" stroke-width="5%" stroke="#e67e22" fill="none" />
<circle class="graph line line-3" cx="50%" cy="50%" r="15%" stroke-width="5%" stroke="#00c0df" fill="none" />
</svg>
CSS
#main {
padding: 100px 0;
margin-top: 100px;
height: 200px;
background-color: pink;
}
.graph {
transform: rotate(270deg);
}
.graph.line {
transform-origin: center;
stroke-dasharray: 160%;
animation: graph 1.5s ease-in-out infinite alternate;
}
#keyframes graph {
from {
stroke-dashoffset: 160%;
}
to {
stroke-dashoffset: 90%;
}
}
That's what viewBox is for. With a viewBox, you establish a local coordinate system, which scales with your image. In your svg you simply use your local coordinates, and the image scales to any size...
#main {
position:absolute;
top:0px;left:0px;
right:0px;bottom:0px;
background:pink
}
.graph {
transform: rotate(270deg);
}
.graph.line {
transform-origin: center;
stroke-dasharray: 160%;
animation: graph 1.5s ease-in-out infinite alternate;
}
#keyframes graph {
from {
stroke-dashoffset: 160%;
}
to {
stroke-dashoffset: 90%;
}
}
<svg viewBox="0 0 100 100" id="main">
<circle class="graph line line-1" cx="50" cy="50" r="25" stroke-width="5" stroke="#f1c40f" fill="none" />
<circle class="graph line line-2" cx="50" cy="50" r="20" stroke-width="5" stroke="#e67e22" fill="none" />
<circle class="graph line line-3" cx="50" cy="50" r="15" stroke-width="5" stroke="#00c0df" fill="none" />
</svg>

Resources