JSFiddle - React, Tailwind, and code Playground
by Ajith S Punalur
HTML
<body>
<div id="wrapper">
<svg id="meter">
<circle id="outline_curves" class="circle outline" cx="50%" cy="50%">
</circle>
<circle id="low" class="circle range" cx="50%" cy="50%" stroke="#FDE47F">
</circle>
<circle id="avg" class="circle range" cx="50%" cy="50%" stroke="#7CCCE5">
</circle>
<circle id="high" class="circle range" cx="50%" cy="50%" stroke="#E04644">
</circle>
<circle id="mask" class="circle" cx="50%" cy="50%" >
</circle>
<circle id="outline_ends" class="circle outline" cx="50%" cy="50%">
</circle>
</svg>
<svg version="1.1" id="meter_needle" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px"
width="16.721px" height="93.834px" viewBox="0 0 16.721 93.834" enable-background="new 0 0 16.721 93.834" xml:space="preserve">
<path fill="#464646" d="M13.886,84.243L2.83,83.875c0,0,3.648-70.77,3.956-74.981C7.104,4.562,7.832,0,8.528,0
c0.695,0,1.752,4.268,2.053,8.894C10.883,13.521,13.886,84.243,13.886,84.243z"/>
<path fill="#464646" d="M16.721,85.475c0,4.615-3.743,8.359-8.36,8.359S0,90.09,0,85.475c0-4.62,3.743-8.363,8.36-8.363
S16.721,80.855,16.721,85.475z"/>
<circle fill="#EEEEEE" cx="8.426" cy="85.471" r="2.691"/>
</svg>
<input id="slider" type="range" min="0" max="100" value="0" />
<label id="lbl" id="value" for="">0%</label>
</div>
<script src="script.js"></script>
</body>
CSS
#wrapper {position: relative; margin: auto;}
#meter {
width: 100%; height: 100%;
transform: rotateX(180deg);
}
.circle {fill: none;}
.outline, #mask {
stroke: #F1F1F1;
stroke-width: 65;
}
.range { stroke-width: 60; }
#slider, #lbl {
position: absolute;
}
#slider {
cursor: pointer;
left: 0;
margin: auto;
right: 0;
top: 58%;
width: 94%;
}
#lbl {
background-color: #4B4C51;
border-radius: 2px;
color: white;
font-family: 'courier new';
font-size: 15pt;
font-weight: bold;
padding: 4px 4px 2px 4px;
right: -48px;
top: 57%;
}
#meter_needle {
height: 40%;
left: 0;
margin: auto;
position: absolute;
right: 0;
top: 10%;
transform-origin: bottom center;
transform: rotate(270deg);
}
input[type=range] {
-webkit-appearance: none;
}
input[type=range]:focus {
outline: none;
}
input[type=range]::-webkit-slider-runnable-track {
width: 100%;
height: 12px;
cursor: pointer;
background: #F6F6F6;
border-radius: 25px;
box-shadow: 0 0 0 1px #f6f6f6;
}
input[type=range]::-webkit-slider-thumb {
box-shadow: 0 0 2px #B2B2B2;
border-radius: 50%;
height: 30px;
width: 30px;
background: #fff;
cursor: pointer;
-webkit-appearance: none;
margin-top: -9px;
}
input[type=range]:focus::-webkit-slider-runnable-track {
background: #F6F6F6;
}
input[type=range]::-moz-range-track {
width: 100%;
height: 12.8px;
cursor: pointer;
background: #F6F6F6;
border-radius: 25px;
}
...
JavaScript
/* set radius for all circles */
var r = 400;
var circles = document.querySelectorAll('.circle');
var total_circles = circles.length;
for (var i = 0; i < total_circles; i++) {
circles[i].setAttribute('r', r);
}
/* set meter's wrapper dimension */
var meter_dimension = (r * 2) + 100;
var wrapper = document.querySelector('#wrapper');
wrapper.style.width = meter_dimension + 'px';
wrapper.style.height = meter_dimension + 'px';
/* add strokes to circles */
var cf = 2 * Math.PI * r;
var semi_cf = cf / 2;
var semi_cf_1by3 = semi_cf / 3;
var semi_cf_2by3 = semi_cf_1by3 * 2;
document.querySelector('#outline_curves')
.setAttribute('stroke-dasharray', semi_cf + ',' + cf);
document.querySelector('#low')
.setAttribute('stroke-dasharray', semi_cf + ',' + cf);
document.querySelector('#avg')
.setAttribute('stroke-dasharray', semi_cf_2by3 + ',' + cf);
document.querySelector('#high')
.setAttribute('stroke-dasharray', semi_cf_1by3 + ',' + cf);
document.querySelector('#outline_ends')
.setAttribute('stroke-dasharray', 2 + ',' + (semi_cf - 2));
document.querySelector('#mask')
.setAttribute('stroke-dasharray', semi_cf + ',' + cf);
/*bind range slider event*/
var slider = document.querySelector('#slider');
var lbl = document.querySelector("#lbl");
var mask = document.querySelector('#mask');
var meter_needle = document.querySelector('#meter_needle');
function range_change_event() {
var percent = slider.value;
var meter_value = semi_cf - ((percent * semi_cf) / 100);
mask.setAttribute('stroke-dasharray', meter_value + ',' + cf);
meter_needle.style.transform = 'rotate(' + (270 + ((percent * 180) / 100)) + 'deg)';
lbl.textContent = percent + '%';
}
slider.addEventListener('input', range_change_event);