I am trying to animate stroke-dasharray of a svg circle based on data values without using libraries.
Data is coming from a javascript calculation, so length of stroke will be different from circle to another.
code will as following: Codepen
or
var circle_1 = 20,
circle_2 = 33,
circle_3 = 42;
document.getElementById('circle_1').innerHTML = circle_1;
document.getElementById('circle_2').innerHTML = circle_2;
document.getElementById('circle_3').innerHTML = circle_3;
.container {
text-align: center;
height: 200px;
}
li {
list-style: none;
display: inline-block;
margin: 100px 50px;
position: relative;
width: 100px;
height: 100px;
}
circle {
stroke: #222;
fill: transparent;
stroke-width: 3px
}
.result {
position: absolute;
top: 40%;
left: 40%;
}
<div class="container">
<li>
<svg width="100" height="100">
<circle cy="50" cx="50" r="40" stroke-dasharray="0em" stroke-dashoffset="0" />
</svg>
<div class="result" id="circle_1"></div>
</li>
<li>
<svg width="100" height="100">
<circle cy="50" cx="50" r="40" stroke-dasharray="0em" stroke-dashoffset="0" />
</svg>
<div class="result" id="circle_2"></div>
</li>
<li>
<svg width="100" height="100">
<circle cy="50" cx="50" r="40" stroke-dasharray="0em" stroke-dashoffset="0" />
</svg>
<div class="result" id="circle_3"></div>
</li>
</div>
If I understand you correctly, you want a pie chart using SVG circle and no library. I worked on something like that recently, here is a mixin I made:
http://codepen.io/Draccoz/pen/GJVJVM
=pie-chart($size, $ring-size, $ring-color: false, $bg-size: 0, $bg-color: transparent)
$d: $size - $ring-size
width: $size
height: $size
>
circle
cx: $size / 2
cy: $size / 2
&:nth-child(1)
r: $bg-size / 2
#if $bg-color
fill: $bg-color
&:nth-child(2)
font-size: $d * 1.01 * $PI
stroke-width: $ring-size
r: $d / 2
fill: transparent
#if $ring-color
stroke: $ring-color
/* stroke-linecap: round */
transition: stroke-dashoffset 1s ease-in-out
transform: translateY($size) rotate(-90deg)
stroke-dasharray: 1em 1em
stroke-dashoffset: 0
#content
Hope it helps.
Related
.showcase-components-colorlist color {
border: 2px solid transparent;
margin: 1px;
padding: 2px;
border-radius: 50%;
cursor: pointer;
}
<div class="showcase-components-colorlist color showcase-components-colorlist color-- active">
<svg width="40" height="40">
<circle cx="20" cy="20" r="19" fill="#c5145d" stroke="#fff" stroke-width="1"></circle>
</svg>
</div>
<div class="showcase-components-colorlist color">
<svg width="40" height="40">
<circle cx="20" cy="20" r="19" fill="#db2586" stroke="#fff" stroke-width="1"></circle>
</svg>
</div>
How to add a check mark to one of the circles as generated by the code above.
One way to do this is through pseudo content. You need to give the element which you'll set the pseudo content a relative container. Then, you create your pseudo content (in this case, a check mark). The positioning will be relative to the parent element's dimensions.
.showcase-components-colorlist {
border: 2px solid transparent;
margin: 1px;
padding: 2px;
border-radius: 50%;
cursor: pointer;
position: relative;
}
.showcase-components-colorlist.selected::before {
content: '✅';
position: absolute;
left: 11px;
top: 9px;
}
<div class="showcase-components-colorlist color showcase-components-colorlist color-- active">
<svg width="40" height="40"><circle cx="20" cy="20" r="19" fill="#c5145d" stroke="#fff" stroke-width="1">
</circle>
</svg>
</div>
<div class="showcase-components-colorlist selected">
<svg width="40" height="40">
<circle cx="20" cy="20" r="19" fill="#db2586" stroke="#fff" stroke-width="1"></circle>
</svg>
</div>
Create your own <svg-option> Native JavaScript Web Component.
<svg-option></svg-option>
<svg-option fill="red" selected></svg-option>
<svg-option fill="rebeccapurple" selected-fill="yellow" selected></svg-option>
creates:
<script>
customElements.define("svg-option",class extends HTMLElement{
connectedCallback(){
this.style.display = "inline-block";
this.innerHTML=`<svg viewBox="0 0 50 50">
<circle cx="50%" cy="50%" r="49%" fill="${this.getAttribute("fill") || "green"}"/>
<circle cx="50%" cy="50%" r="30%" fill="${this.getAttribute("selected-fill") || "beige"}" visibility="hidden"/>
</svg>`;
this.select();
this.onclick = (evt) => this.toggle();
}
select(state = this.hasAttribute("selected")) {
this.querySelector("circle:nth-child(2)").setAttribute("visibility" , state ? "visible" :"hidden");
}
toggle(){
this.select( this.toggleAttribute("selected") );
}
})
</script>
<style>
svg-option {
--svg-option-size:180px;
width: var(--svg-option-size);
height: var(--svg-option-size);
cursor: pointer;
}
svg-option[selected]{
background:lightgreen;
}
svg-option:not([selected]){
background:pink;
}
</style>
<svg-option></svg-option>
<svg-option fill="red" selected></svg-option>
<svg-option fill="rebeccapurple" selected-fill="yellow" selected></svg-option>
I can change the top property of percent1 with
document.getElementById('percent1').style.top = '50px';
HTML:
<div class="box" id="box1">
<div class="percent" id="percent1">
<svg>
<circle cx="70" cy="70" r="70"></circle>
<circle cx="70" cy="70" r="70"></circle>
</svg>
<div class="num">
<h2>45<span>%</span></h2>
</div>
</div>
<h2 class="text">Percentage</h2>
</div>
CSS to modify:
.box .percent svg circle:nth-child(2)
{
stroke-dashoffset:calc(440 - (440 * 15) / 100);
}
How can I change "stroke-dashoffset" value when it's nested like this?
It would appear :nth-child targeting matches elements based upon their position, not on their properties so I think what you want isn't possible with this approach.
Could consider attaching some other type of targeting mechanism (either in HTML or JS) to target the element you want to style more granularly in CSS.
#percent1 {
position: absolute;
top: 50px;
}
#percent1>svg {
overflow: visible;
}
#percent1>svg>circle {
fill: blue;
}
#percent1>svg>circle:nth-child(2) {
fill: yellow;
}
#circle-target {
stroke-dashoffset: 100;
stroke: red;
stroke-width: 8px;
}
<div class="box" id="box1">
<div class="percent" id="percent1">
<svg>
<circle cx="70" cy="70" r="70"></circle>
<circle cx="70" cy="70" r="70" id="circle-target"></circle>
</svg>
</div>
You can do it with
document.querySelectorAll(".box .percent svg circle:nth-child(2)")
I have a design I'm trying to port over to code, the design is as follows:
I've created the majority of the design in code already (just a prototype so the html/css isn't perfect), but the area I'm struggling with is the dotted progression that you can see within the curved path
.frame {
width: 375px;
height: 750px;
background: #171B42;
background: linear-gradient(to bottom, #171B42, #171B42 50%, #3C98FF 110%);
position: relative;
}
#earth {
position: absolute;
bottom: 0;
left: 0;
right: 0;
left: 50%;
-webkit-transform: translateX(-50%);
transform: translateX(-50%)
}
.locked {
text-align: center;
margin: 76px auto;
width: 230px;
position: relative;
}
.locked img {
width: 60px;
margin: 0 26px;
}
.locked.row-1:after {
content: "";
width: 163px;
height: 175px;
background: url(https://i.imgur.com/u6v9Dkd.png) center center no-repeat;
background-size: cover;
position: absolute;
top: -130px;
right: -50px;
bottom: 0;
z-index: -1;
}
.locked.row-2:after {
content: "";
width: 163px;
height: 175px;
background: url(https://i.imgur.com/u6v9Dkd.png) center center no-repeat;
background-size: cover;
position: absolute;
top: -127px;
left: -46px;
bottom: 0;
z-index: -1;
transform: rotate(180deg);
}
.path-map {
position: absolute;
bottom: 180px;
left: 0;
right: 0;
left: 50%;
-webkit-transform: translateX(-50%);
transform: translateX(-50%);
width:375px;
}
<div class="frame">
<div class="path-map">
<div class="locked row-2">
<img src="https://i.imgur.com/h3ElY2f_d.webp?maxwidth=728&fidelity=grand">
<img src="https://i.imgur.com/h3ElY2f_d.webp?maxwidth=728&fidelity=grand">
</div>
<div class="locked row-1">
<img src="https://i.imgur.com/h3ElY2f_d.webp?maxwidth=728&fidelity=grand">
</div>
</div>
<img id="earth" src="https://i.imgur.com/RR8kQx1_d.webp?maxwidth=728&fidelity=grand"/>
</div>
I'm wondering if anyone has any ideas on how to create that dotted path, it might require some javascript? I'm not entirely sure what the best way of solving this would be outside of just manually adding each dot with position: absolute.
Any help would be amazing, thank you!
Here's an example of how this can be done using SVG <path> elements, <use> elements, a <mask> element, the stroke-dasharray attribute, and the stroke-linecap attribute.
You can define a single <path> element once, and then reuse it multiple times with different stroke styles or masks applied to it, which is especially handy in this case.
body { background: darkblue; }
<svg viewBox="0 0 515 515">
<defs>
<mask id="mask">
<rect fill="black" x="0" y="0" width="515" height="515"></rect>
<rect fill="white" x="0" y="200" width="515" height="315"></rect>
</mask>
<path id="path" fill="none" d="M138 414C192.333 414 312.8 414 360 414C419 414 504 262 360 262C216 262 261 262 153 262C45 262 39 130 144 130C249 130 327 130 378 130C429 130 452 83 452 -2C452 -87 472 -87 452 -87"></path>
</defs>
<!-- solid wide line -->
<use href="#path" stroke="rgba(255,255,255,0.2)" stroke-width="20"></use>
<!-- solid narrow line -->
<use href="#path" stroke="rgba(255,255,255,0.2)" stroke-width="10"></use>
<!-- dotted full line -->
<use href="#path" stroke="rgba(255,255,255,0.2)" stroke-width="5" stroke-dasharray="0 10" stroke-linecap="round"></use>
<!-- dotted masked line -->
<use href="#path" stroke="rgba(255,255,255,0.8)" stroke-width="5" stroke-dasharray="0 10" stroke-linecap="round" mask="url(#mask)"></use>
</svg>
I'm working to create an SVG circle progress indicator in react... Here is my output:
CODEPEN
The problem is I need the red stroke to start at the top, not at the current 90% angle... Is there some CSS I can add to reposition the red stroke to start at the top?
FYI, I'm using the following component in react to render this HTML: https://github.com/wmartins/react-circular-progress/blob/gh-pages/src/js/components/CircularProgress.jsx.js
codepen source below
html
<svg class="CircularProgress" width="50" height="50" viewBox="0 0 50 50">
<circle class="CircularProgress-Bg" cx="25" cy="25" r="24" stroke-width="2px"></circle>
<circle class="CircularProgress-Fg" cx="25" cy="25" r="24" stroke-width="2px" style="stroke-dasharray: 150.796; stroke-dashoffset: 125.161;"></circle>
<text class="CircularProgress-Text" x="25" y="25" dy=".4em" text-anchor="middle">17%</text>
</svg>
css
.CircularProgress-Bg,
.CircularProgress-Fg {
fill: none;
}
.CircularProgress-Bg {
stroke: #CCC;
}
.CircularProgress-Fg {
transition: stroke-dashoffset .5s ease-in-out;
stroke: red;
}
.CircularProgress-Text {
font-size: 15px;
font-weight: 600;
fill: rgba(255,255,255,0.9);
transform: translate(0 50%);
}
You could use transform, add transform="rotate(-90 25 25)" and it will start at the top
.CircularProgress-Bg,
.CircularProgress-Fg {
fill: none;
}
.CircularProgress-Bg {
stroke: #CCC;
}
.CircularProgress-Fg {
transition: stroke-dashoffset .5s ease-in-out;
stroke: red;
}
.CircularProgress-Text {
font-size: 15px;
font-weight: 600;
fill: rgba(255,255,255,0.9);
transform: translate(0 50%);
}
<svg class="CircularProgress" width="50" height="50" viewBox="0 0 50 50">
<circle class="CircularProgress-Bg" cx="25" cy="25" r="24" stroke-width="2px"></circle>
<circle transform="rotate(-90 25 25)" class="CircularProgress-Fg" cx="25" cy="25" r="24" stroke-width="2px" style="stroke-startOffest: 50%;stroke-dasharray: 150.796; stroke-dashoffset: 125.161;"></circle>
<text class="CircularProgress-Text" x="25" y="25" dy=".4em" text-anchor="middle">17%</text>
</svg>
If to use CSS, you can rotate the svg element instead of the circle (which might or might not be possible based on its shape)
.CircularProgress {
transform: rotate(-90deg);
}
.CircularProgress-Bg,
.CircularProgress-Fg {
fill: none;
}
.CircularProgress-Bg {
stroke: #CCC;
}
.CircularProgress-Fg {
transition: stroke-dashoffset .5s ease-in-out;
stroke: red;
}
.CircularProgress-Text {
font-size: 15px;
font-weight: 600;
fill: rgba(255,255,255,0.9);
transform: translate(0, 50%);
}
<svg class="CircularProgress" width="50" height="50" viewBox="0 0 50 50">
<circle class="CircularProgress-Bg" cx="25" cy="25" r="24" stroke-width="2px"></circle>
<circle class="CircularProgress-Fg" cx="25" cy="25" r="24" stroke-width="2px" style="stroke-dasharray: 150.796; stroke-dashoffset: 125.161;"></circle>
<text class="CircularProgress-Text" x="25" y="25" dy=".4em" text-anchor="middle">17%</text>
</svg>
I have the following code:
<div id="parent">
<div class="child" id="air1">Medium 1</div>
<div class="child" id="glass">Medium 2</div>
<div class="child" id="air2">Medium 1</div>
</div>
<style>
#parent {
background: #999;
padding: 0px;
}
#glass {
background: #666;
}
.child {
background: #ccc;
height: 200px;
margin: 0px;
}
</style>
I want to draw an arrow from #air1 into #glass using an svg. I added the following code into the div to draw an example arrow:
<svg width="300" height="100">
<defs>
<marker id="arrow" markerWidth="13" markerHeight="13" refx="2" refy="6" orient="auto">
<path d="M2,2 L2,11 L10,6 L2,2" style="fill:red;" />
</marker>
</defs>
<path d="M30,150 L100,50"
style="stroke:red; stroke-width: 1.25px; fill: none;
marker-end: url(#arrow);"
/>
</svg>
I don't want the arrow pointed in a random direction though, I want it to point into #glass like this:
Also, how would I go about drawing a less steep arrow like this as well:
How can I do this?
You can achieve that by using positioning (inserting the svg into the first section and set it to position: absolute;) and adjusting the offset of the path element. To make the arrow pointing down, just use a negative value for the second value of the path description attribute.
For more information see w3schools about path.
#parent {
background: #999;
padding: 0px;
}
#glass {
background: #666;
}
.child {
background: #ccc;
height: 200px;
margin: 0px;
position: relative;
}
svg {
position: absolute;
left: 0;
}
<div id="parent">
<div class="child" id="air1">Medium 1
<svg width="400" height="200">
<defs>
<marker id="arrow" markerWidth="13" markerHeight="13" refx="2" refy="6" orient="auto">
<path d="M2,2 L2,11 L10,6 L2,2" style="fill:red;" />
</marker>
</defs>
<path d="M-600,-10 L300,195" style="stroke:red; stroke-width: 1.25px; fill: none; marker-end: url(#arrow);" />
</svg>
</div>
<div class="child" id="glass">Medium 2</div>
<div class="child" id="air2">Medium 1</div>
</div>