Create a circle progressbar in svg or css [duplicate] - html

This question already has answers here:
CSS Progress Circle
(8 answers)
Closed 4 years ago.
I try to design a circle progressbar with some information inside. Something like this.
I have svg but I cant write inside circle also. The start and end point distance is very low. I am looking for something like image.
svg {
height: 200px;
margin: auto;
display: block;
}
path {
stroke-linecap: round;
stroke-width: 2;
}
path.grey {
stroke: lightgrey;
}
path.purple {
stroke: purple;
stroke-dasharray: calc(40 3.142 1.85);
stroke-dashoffset: 80;
/ adjust last number for variance /
}
<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" viewbox="0 0 100 100">
<path class="grey" d="M40,90
A40,40 0 1,1 70,90"
style="fill:none;"/>
<path class="purple" d="M40,90
A40,40 0 1,1 70,90"
style="fill:none;"/>
</svg>

This is my solution; In order to calculate the path's length you may use the path.getTotalLength() method.
In order to center the text around a point (the center of the SVG canvas in this case) use dominant-baseline="middle" text-anchor="middle"
theRange.addEventListener("input",()=>{
let v=220.6 - map(theRange.value,0,100,0,220.6);
thePath.style.strokeDashoffset = v
theText.textContent = theRange.value+"%"
})
function map(n, a, b, _a, _b) {
let d = b - a;
let _d = _b - _a;
let u = _d / d;
return _a + n * u;
}
svg {
height: 200px;
margin: auto;
display: block;
border:1px solid;
overflow:visible
}
path {
stroke-linecap: round;
stroke-width: 2;
}
.grey {
stroke: lightgrey;
}
.purple {
stroke: purple;
stroke-dasharray: 220.6;
stroke-dashoffset: 44.12;
}
p{text-align:center}
<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" viewBox="0 0 110 110">
<defs>
<path id="thePath" d="M40,90
A40,40 0 1,1 70,90"
style="fill:none;"/>
</defs>
<use xlink:href="#thePath" id="base" class="grey" />
<use xlink:href="#thePath" id="slider" class="purple" />
<text id="theText" x="55" y="55" dominant-baseline="middle" text-anchor="middle">80%</text>
</svg>
<p><input id="theRange" type="range" min="0" max="100" value="80" step=".1" /></p>

Related

How to make inline SVG 100% of parent container

I'm trying to make an inline SVG scale to the width of the parent element. I feel like the circle elements are causing me the issue, but I'm unsure what I should be changing to achieve it.
I have set the container to 300x300px, I have set the viewBox to "0 0 300 300". I assume I have to set the r, cx, and cy to half those? to which I have set to "150" but the circle is now getting cut off.
I have been going round in circles (excuse the pun) changing dimensions but no luck.
Your help will be greatly appreciated.
Please find a link to my codepen: https://codepen.io/MayhemBliz/pen/NWyNGxj
function circle() {
const progressRing = document.querySelector('.progress-ring');
const circle = document.querySelector('.progress-ring__bar');
const r = circle.getAttribute('r');
const percent = progressRing.dataset.percent;
const c = Math.PI * r * 2;
const pct = ((0 - percent) / 100) * c;
circle.style.strokeDashoffset = pct;
//const percentageText = document.querySelector('.percentage');
//percentageText.textContent = percent + "%";
}
window.addEventListener('load', circle);
.progress-ring {
width: 300px;
height: 300px;
display: flex;
align-items: center;
justify-content: center;
margin: 0 auto;
}
.progress-ring__svg {
/*transform: rotate(-90deg);*/
}
.progress-ring__bar-bg, .progress-ring__bar {
stroke-dashoffset: 0;
transition: stroke-dashoffset 1s linear;
stroke: #FF9F1E;
stroke-width: 1em;
}
.progress-ring__bar {
stroke: #666;
}
.percentage {
position: absolute;
font-size: 2.5rem;
font-weight: bold;
}
<div class="progress-ring" data-percent="80">
<svg class="progress-ring__svg" viewBox="0 0 300 300" version="1.1" xmlns="http://www.w3.org/2000/svg">
<circle class="progress-ring__bar-bg" r="150" cx="150" cy="150" fill="transparent" stroke-dasharray="565.48" stroke-dashoffset="0"></circle>
<circle class="progress-ring__bar" r="150" cx="150" cy="150" fill="transparent" stroke-dasharray="565.48" stroke-dashoffset="0"></circle>
</svg>
<span class="percentage">80%</span>
</div>
To avoid overflow you could use percentage based values for stroke-width and r (radius) like so:
<circle r="47.5%" cx="50%" cy="50%" />
or a pixel based approximation like
<circle r="142" cx="150" cy="150" />
function circle() {
const progressRing = document.querySelector('.progress-ring');
const circle = document.querySelector('.progress-ring__bar');
const r = circle.getAttribute('r');
const percent = progressRing.dataset.percent;
const c = Math.PI * r * 2;
const pct = ((0 - percent) / 100) * c;
circle.style.strokeDashoffset = pct;
//const percentageText = document.querySelector('.percentage');
//percentageText.textContent = percent + "%";
}
window.addEventListener('load', circle);
.progress-ring {
width: 300px;
/*height: 300px;*/
display: flex;
align-items: center;
justify-content: center;
margin: 0 auto;
max-width:100%;
}
.progress-ring__svg {
//width:100%;
/*transform: rotate(-90deg);*/
}
.progress-ring__bar-bg, .progress-ring__bar {
stroke-dashoffset: 0;
transition: stroke-dashoffset 1s linear;
stroke: #FF9F1E;
stroke-width: 5%;
}
.progress-ring__bar {
stroke: #666;
}
.percentage {
position: absolute;
font-size: 2.5rem;
font-weight: bold;
}
.resize{
resize:both;
overflow:auto;
border: 1px solid #ccc;
padding:1em;
}
<div class="resize">
<div class="progress-ring" data-percent="80">
<svg class="progress-ring__svg" viewBox="0 0 300 300" version="1.1" xmlns="http://www.w3.org/2000/svg">
<circle class="progress-ring__bar-bg" r="47.5%" cx="50%" cy="50%" fill="transparent" stroke-dasharray="565.48" stroke-dashoffset="0"></circle>
<circle class="progress-ring__bar" r="142" cx="150" cy="150" fill="transparent" stroke-dasharray="565.48" stroke-dashoffset="0"></circle>
<text class="percentage" x="50%" y="50%" text-anchor="middle" dominant-baseline="middle" dy="2%">80%</text>
</svg>
</div>
</div>
<p>Resize me</p>
However, you might also use a <text> element instead of a <span> to keep the percentage text size relative to you svg boundary.

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.

How to put an image in SVG rounded corner hexagon using HTML and CSS?

I am trying to achieve something like the below using SVG:
I have tried many solutions from this platform and so far, I have accomplished this:
<svg xmlns="http://www.w3.org/2000/svg" width="327.846" height="318.144" viewBox="0 0 327.846 318.144">
<defs>
<style>
.a {
fill:#000;
stroke-width: 25px;
stroke: rgba(255, 255, 255, 0.5);
}
</style>
</defs>
<path class="a" d="M172.871,0a28.906,28.906,0,0,1,25.009,14.412L245.805,97.1a28.906,28.906,0,0,1,0,28.989L197.88,208.784A28.906,28.906,0,0,1,172.871,223.2H76.831a28.906,28.906,0,0,1-25.009-14.412L3.9,126.092A28.906,28.906,0,0,1,3.9,97.1L51.821,14.412A28.906,28.906,0,0,1,76.831,0Z" transform="translate(111.598) rotate(30)"/>
</svg>
With the above code, I am able to add stroke but not image. And for below code, I am able to add the image but stroke is not appearing:
<svg xmlns="http://www.w3.org/2000/svg" width="327.846" height="318.144" viewBox="0 0 327.846 318.144">
<defs>
<style>
.a {
fill: #000;
stroke-width: 25px;
stroke: rgba(255, 255, 255, 0.5);
}
</style>
<clipPath id="image">
<path class="a"
d="M172.871,0a28.906,28.906,0,0,1,25.009,14.412L245.805,97.1a28.906,28.906,0,0,1,0,28.989L197.88,208.784A28.906,28.906,0,0,1,172.871,223.2H76.831a28.906,28.906,0,0,1-25.009-14.412L3.9,126.092A28.906,28.906,0,0,1,3.9,97.1L51.821,14.412A28.906,28.906,0,0,1,76.831,0Z"
transform="translate(111.598) rotate(30)" />
</clipPath>
</defs>
<image clip-path="url(#image)" height="100%" width="100%" xlink:href="http://placekitten.com/800/800"
preserveAspectRatio="xMidYMin slice"></image>
</svg>
What am I missing to get both onboard?
PS: Rounded corners are a must for the outer core and transparent layer.
Any help/suggestions will be very helpful. Thanks.
Set fill: transparent / fill: none for the path and use it again, as a child of svg
<svg xmlns="http://www.w3.org/2000/svg" width="327.846" height="318.144" viewBox="0 0 327.846 318.144">
<defs>
<style>
.a {
fill: none;
stroke-width: 25px;
stroke: rgba(255, 255, 255, 0.5);
}
</style>
<clipPath id="image">
<path transform="translate(111.598) rotate(30)" d="M172.871,0a28.906,28.906,0,0,1,25.009,14.412L245.805,97.1a28.906,28.906,0,0,1,0,28.989L197.88,208.784A28.906,28.906,0,0,1,172.871,223.2H76.831a28.906,28.906,0,0,1-25.009-14.412L3.9,126.092A28.906,28.906,0,0,1,3.9,97.1L51.821,14.412A28.906,28.906,0,0,1,76.831,0Z"/>
</clipPath>
</defs>
<image clip-path="url(#image)" height="100%" width="100%" xlink:href="http://placekitten.com/800/800" preserveAspectRatio="xMidYMin slice"></image>
<path class="a" d="M172.871,0a28.906,28.906,0,0,1,25.009,14.412L245.805,97.1a28.906,28.906,0,0,1,0,28.989L197.88,208.784A28.906,28.906,0,0,1,172.871,223.2H76.831a28.906,28.906,0,0,1-25.009-14.412L3.9,126.092A28.906,28.906,0,0,1,3.9,97.1L51.821,14.412A28.906,28.906,0,0,1,76.831,0Z" transform="translate(111.598) rotate(30)"/>
</svg>
comments on #Vishal Bhatt
One more queue: when I increase stroke-width, the curve on the
transparent layer becomes a sharp corner. How can I keep that rounded
when increasing stroke-width?
will help solve the problem - stroke-linejoin:round;
Used a wide stroke of 65px while keeping the inner rounding of the stroke
For solution instead of clip-Path one can try mask:
<svg xmlns="http://www.w3.org/2000/svg" width="327.846" height="318.144" viewBox="0 0 327.846 318.144">
<defs>
<style>
.a {
fill:white;
stroke-width: 65px;
stroke: rgba(255, 255, 255, 0.5);
stroke-linejoin:round;
}
</style>
<mask id="msk">
<path class="a" transform="translate(111.598) rotate(30)" d="M172.871,0a28.906,28.906,0,0,1,25.009,14.412L245.805,97.1a28.906,28.906,0,0,1,0,28.989L197.88,208.784A28.906,28.906,0,0,1,172.871,223.2H76.831a28.906,28.906,0,0,1-25.009-14.412L3.9,126.092A28.906,28.906,0,0,1,3.9,97.1L51.821,14.412A28.906,28.906,0,0,1,76.831,0Z"/>
</mask>
</defs>
<image mask="url(#msk)" height="100%" width="100%" xlink:href="http://placekitten.com/800/800" preserveAspectRatio="xMidYMin slice"></image>
</svg>
Update
Hexagon rotation command
transform="rotate(15 124.5 111.5)", here
15 - angle of rotation
124.5 111.5 - Hexagon rotation center coordinates
<svg xmlns="http://www.w3.org/2000/svg" width="327.846" height="318.144" viewBox="0 0 327.846 318.144">
<defs>
<style>
.a {
fill:white;
stroke-width: 45px;
stroke: rgba(255, 255, 255, 0.5);
stroke-linejoin:round;
}
</style>
<mask id="msk">
<path class="a" transform="translate(50 50) rotate(15 124.5 111.5)" d="M172.871,0a28.906,28.906,0,0,1,25.009,14.412L245.805,97.1a28.906,28.906,0,0,1,0,28.989L197.88,208.784A28.906,28.906,0,0,1,172.871,223.2H76.831a28.906,28.906,0,0,1-25.009-14.412L3.9,126.092A28.906,28.906,0,0,1,3.9,97.1L51.821,14.412A28.906,28.906,0,0,1,76.831,0Z"/>
</mask>
</defs>
<image mask="url(#msk)" height="100%" width="100%" xlink:href="http://placekitten.com/800/800" preserveAspectRatio="xMidYMin slice"></image>
</svg>
Problem with clip-path or masks, is they need to have unique ids if you have multiple SVGs on the page.
One way around this is to create the SVG client-side; and generate a unique id for every clip-path
While we're at it might as well simplify that Hexagon path
Wrapped in a Custom Element (no shadowDOM required with that unique id) you get:
<style>
svg {
width: 148px;
background: teal;
}
</style>
<svg-hexed-image ></svg-hexed-image>
<svg-hexed-image src="http://placekitten.com/800/800"></svg-hexed-image>
<svg-hexed-image rotate="30" src="http://placekitten.com/801/801"></svg-hexed-image>
<svg-hexed-image rotate="45" stroke="red" opacity=".5" src="http://placekitten.com/300/300"></svg-hexed-image>
<script>
customElements.define('svg-hexed-image', class extends HTMLElement {
connectedCallback() {
let img = this.getAttribute("src") || "http://placekitten.com/120/120";
let strokewidth = this.getAttribute("stroke-width") || "3";
let opacity = this.getAttribute("opacity") || ".5";
let stroke = this.getAttribute("stroke") || "white";
let rotate = this.getAttribute("rotate") || 0;
let transform = `transform="rotate(${rotate} 23 23)"`;
// make very sure for a unique id:
let id = "id" + btoa(img) + (new Date() / 1);
let d = `M31 3.5a5 5 90 014 3l8 14a5 5 90 010 5l-8 14a5 5 90 01-4 3h-16a5 5 90 01-4-3l-8-14a5 5 90 010-5l8-14a5 5 90 014-3z`;
// now write HTML:
this.innerHTML = `<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 46 46">
<defs><clipPath id="${id}"><path fill="none" ${transform} d="${d}"></clipPath></defs>
<image clip-path="url(#${id})" height="100%" width="100%" href="${img}"></image>
<path fill="none" stroke="${stroke}" stroke-width="${strokewidth}"
${transform} opacity="${opacity}" d="${d}"/></svg>`;
}
});
</script>

How do I convert these SVG properties into CSS declarations?

I have the following HTML:
<svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
<circle cx="10.5" cy="10.5" r="7.5"></circle>
<line x1="21" y1="21" x2="15.8" y2="15.8"></line>
</svg>
The HTML looks clunky at best, how do I convert it's properties into CSS?
For example:
#search-svg {
width: 20;
height: 20;
viewBox: 0 0 24 24;
fill: none;
stroke: currentColor;
stroke-width: 2;
stroke-linecap: round;
stroke-linejoin: round
}
I know that the width, height and fill properties exist and can be converted into CSS.
However I don't know how the other properties work or what their CSS equivalents are, and a quick search on caniuse yeilds no results for CSS properties beginning with stroke.
Thank you.
I checked it and almost all properties from your example works, you have to remember that size properties needs unit eg. px.
svg {
width: 40px;
height: 40px;
fill: red;
stroke: green;
stroke-width: 4px;
stroke-linecap: round;
stroke-linejoin: round;
}
circle {
cx: 12px;
cy: 13px;
r: 6px;
}
Also found an source of more data about SVG properties https://css-tricks.com/svg-properties-and-css/
If you want to change the svg css, you should also change the child tag css.
#search-svg {
width: 50px;
height: 50px;
}
#search-svg circle {
fill:none;
stroke: currentColor;
stroke-width: 3;
stroke-linecap: round;
stroke-linejoin: round
}
#search-svg line{
stroke: currentColor;
stroke-linecap: round;
stroke-linejoin: round
}
<!DOCTYPE html>
<html>
<head>
<title>Test</title>
</head>
<body>
<svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" id ="search-svg">
<circle cx="10.5" cy="10.5" r="7.5"></circle>
<line x1="21" y1="21" x2="15.8" y2="15.8"></line>
</svg>
</body>
</html>

Create and Filling arc progressively with css - circle progress bar

I'm struggling to fill an arc like a progress bar because I'm not that good working with css.
I want to achieve a "progress" arc like this:
I came across with this: https://codepen.io/enslavedeagle/pen/AXzaKE
#arc,
#arc::before {
display: block;
box-sizing: border-box;
border-radius: 100%;
width: 100px;
height: 100px;
position: absolute;
background-color: transparent;
padding: 0;
margin: 0;
}
#arc {
border: solid #00BBEE 12px;
clip: rect(0px, 100px, 50px, 0px);
margin: 25px;
}
#arc::before {
content: '';
border: solid black 12px;
top: -12px;
left: -12px;
clip: rect(0px, 100px, 50px, 0px);
transform: rotate(-150deg);
/* define the fill length, using the rotation above.
from -180deg (0% fill) to 0deg (100% fill) */
/* if you have a better solution to make thing like this
work, please let me know! :) */
}
and try to customize to be like what I want to but with no sucess until now: here: https://codepen.io/anon/pen/qpNrEP
Could any one give some help with this? Can also be alternative solution to achieve this.
I appreciate
Kind regards,
You can use an SVG with two arc on on top of the other and then use stroke-dash-array.
svg {
height: 90vh;
margin: auto;
display: block;
}
path {
stroke-linecap: round;
stroke-width: 2;
}
path.grey {
stroke: lightgrey;
}
path.purple {
stroke: purple;
stroke-dasharray: calc(40 * 3.142 * 1.85);
stroke-dashoffset: 20;
/* adjust last number for variance */
}
<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" viewbox="0 0 100 100">
<path class="grey" d="M40,90
A40,40 0 1,1 60,90"
style="fill:none;"/>
<path class="purple" d="M40,90
A40,40 0 1,1 60,90"
style="fill:none;"/>
</svg>
Using SVG is the best way to create an arc like this.
Here is the solution with all iterations of the loader:
.progress-wrapper {
height: 100vh;
display: flex;
align-items: center;
justify-content: space-around;
}
path {
stroke-linecap: round;
stroke-width: 6;
}
.grey {
stroke: #e5e5e5;
}
.red {
stroke: #e33800;
stroke-dasharray: 248;
stroke-dashoffset: 240;
/* adjust last number for variance */
}
.red-02 {
stroke-dashoffset: 220;
}
.red-03 {
stroke-dashoffset: 200;
}
.red-04 {
stroke-dashoffset: 180;
}
.red-05 {
stroke-dashoffset: 160;
}
.red-06 {
stroke-dashoffset: 140;
}
.red-07 {
stroke-dashoffset: 120;
}
.red-08 {
stroke-dashoffset: 100;
}
.red-09 {
stroke-dashoffset: 50;
}
.red-10 {
stroke-dashoffset: 0;
}
<div class="progress-wrapper">
<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px" viewBox="37 -5 120 100" width="120" height="100">
<path class="grey" d="M55,90
A55,55 0 1,1 140,90"
style="fill:none;"/>
<path class="red" d="M55,90
A55,55 0 1,1 140,90"
style="fill:none;"/>
</svg>
<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px" viewBox="37 -5 120 100" width="120" height="100">
<path class="grey" d="M55,90
A55,55 0 1,1 140,90"
style="fill:none;"/>
<path class="red red-02" d="M55,90
A55,55 0 1,1 140,90"
style="fill:none;"/>
</svg>
<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px" viewBox="37 -5 120 100" width="120" height="100">
<path class="grey" d="M55,90
A55,55 0 1,1 140,90"
style="fill:none;"/>
<path class="red red-03" d="M55,90
A55,55 0 1,1 140,90"
style="fill:none;"/>
</svg>
<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px" viewBox="37 -5 120 100" width="120" height="100">
<path class="grey" d="M55,90
A55,55 0 1,1 140,90"
style="fill:none;"/>
<path class="red red-04" d="M55,90
A55,55 0 1,1 140,90"
style="fill:none;"/>
</svg>
<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px" viewBox="37 -5 120 100" width="120" height="100">
<path class="grey" d="M55,90
A55,55 0 1,1 140,90"
style="fill:none;"/>
<path class="red red-05" d="M55,90
A55,55 0 1,1 140,90"
style="fill:none;"/>
</svg>
<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px" viewBox="37 -5 120 100" width="120" height="100">
<path class="grey" d="M55,90
A55,55 0 1,1 140,90"
style="fill:none;"/>
<path class="red red-06" d="M55,90
A55,55 0 1,1 140,90"
style="fill:none;"/>
</svg>
<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px" viewBox="37 -5 120 100" width="120" height="100">
<path class="grey" d="M55,90
A55,55 0 1,1 140,90"
style="fill:none;"/>
<path class="red red-07" d="M55,90
A55,55 0 1,1 140,90"
style="fill:none;"/>
</svg>
<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px" viewBox="37 -5 120 100" width="120" height="100">
<path class="grey" d="M55,90
A55,55 0 1,1 140,90"
style="fill:none;"/>
<path class="red red-08" d="M55,90
A55,55 0 1,1 140,90"
style="fill:none;"/>
</svg>
<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px" viewBox="37 -5 120 100" width="120" height="100">
<path class="grey" d="M55,90
A55,55 0 1,1 140,90"
style="fill:none;"/>
<path class="red red-09" d="M55,90
A55,55 0 1,1 140,90"
style="fill:none;"/>
</svg>
<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px" viewBox="37 -5 120 100" width="120" height="100">
<path class="grey" d="M55,90
A55,55 0 1,1 140,90"
style="fill:none;"/>
<path class="red red-10" d="M55,90
A55,55 0 1,1 140,90"
style="fill:none;"/>
</svg>
</div>
https://codepen.io/shalinigandhi/pen/mdmmwdV
I've just created a plnker in Angular 2 which I think it's exactly what you want.
It manage the fill of the second arc with a variable:
<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" viewbox="0 0 100 100">
<path class="grey" d="M40,90 A40,40 0 1,1 60,90" style="fill:none;"/>
<path [ngStyle]="{'stroke-dashoffset':percentPath}" class="blue" d="M40,90 A40,40 0 1,1 60,90" style="fill:none;"/>
</svg>
Then, in the component I call a function which take the value from a slider with min=0 and max=100:
this.percentPath=(233-(V*2.33)); //233 is the maximun number stroke-dashoffset needs to disapear the second path
https://plnkr.co/edit/KNPThi?p=info
take it a look!
After not finding the answer I liked here I did some work myself and made a Codepen that you can use that makes progress bars exactly as you described with the inputs being: the amount of degrees of a circle that should be 100%, the radius of the circle and the percentage you to have filled.
Progress bar example (image)
HTML:
<section class="stat" id="sectionId">
<svg viewbox="0 0 100 100">
<path class="bar" d="
M 10, 50
a 40,40 0 1,0 80,0
a 40,40 0 1,0 -80,0
"/>
<path class="progress" d="
M 10, 50
a 40,40 0 1,0 80,0
a 40,40 0 1,0 -80,0
"/>
</svg>
<script type="text/javascript">
_.makeProgressBar("01", 240, 40, 86);
</script>
</section>
CSS:
.stat {
width: 200px;
}
svg {
display: block;
transform-origin: center center;
transform: rotate( 90deg );
}
path {
stroke-linecap: round;
stroke-width: 6px ;
fill:none;
}
JS:
_ = {};
//this is a self executing function, it keeps the global namespace clean
(function(win, doc, helper){
helper.makeProgressBar = function(id, totalDegrees, radius, percentage) {
var section = doc.getElementById(id);
var svg = section.children[0];
var bar = svg.children[0];
var progress = svg.children[1];
if(percentage > 100){
percentage = 100;
}
if(percentage < 0){
percentage = 0;
}
var percentageDegrees = calculateDegreesFromPercentage(totalDegrees, percentage);
var barDash = calculateDash(totalDegrees, radius);
var barGap = calculateDash ( 360 - totalDegrees, radius) * 2;
var progressDash = calculateDash(percentageDegrees, radius);
var progressGap = calculateDash(360 - percentageDegrees, radius) * 2;
var rotation = 0 - ((totalDegrees - percentageDegrees) / 2);
bar.setAttribute("style", "stroke: #707070; fill: none; stroke-dasharray: " + barDash + " " + barGap + ";");
progress.setAttribute("style", "stroke: #23CE39; fill: none; stroke-dasharray: " + progressDash + " " + progressGap + "; transform-origin: center center; transform: rotate("+ rotation +"deg)");
}
calculateDegreesFromPercentage = function(totalDegrees, percentage) {
return totalDegrees/100*percentage;
}
calculateDash = function(degrees, radius) {
return degrees * Math.PI / 180 * 0.5 * radius;
}
})(window, document, _);