There are certain kinds of measurements, such as time, percentage, and angle, that are best displayed visually, often in an arc or circle. HTML5 doesn’t feature a native radial control, but that doesn’t prevent you from creating your own; in this example, I’ll show how to present an interactive percentage or timer value.
The control for this is a range input:
<input type="range" min="0" max="100" value="15" id="utilslider">
…and the readout an SVG element, derived from a solution proposed by Lea Verou in her excellent CSS Secrets:
<div id="readout">
<svg viewBox="0 0 100 100">
<circle r="25" cx="50" cy="50" id="pie" />
</svg>
</div>
(The <div>
is used for positioning the SVG and other elements, as detailed in the related CodePen demo).
Styling The Circle
The perimeter of the SVG circle element is 2πr, i.e. 2 × 3.14 × 25, or approximately 158. With that in mind, we can make the SVG appear as a circle using border-radius
, and stroke the outside of it, using a dasharray
to show just a portion of the stroke:
#readout {
position: relative;
}
svg {
transform: rotate(-90deg);
color: #333;
background: currentcolor;
border-radius: 50%;
width: 100%;
}
#pie {
fill: currentcolor;
stroke: hsl(0,0%,50%);
stroke-width: 50;
stroke-dasharray: 55 158;
}
Note that I’ve used currentcolor
to define both the svg and the circle
element with a CSS variable. The stroke-width
is the diameter of the circle: this is necessary due to the fact that SVG 1.1 strokes are always through the exact center of the associated path. The border-radius
of the SVG “clips” the outside edge of this stroke, while the rotation brings it “upright”, so that the visible stroke, which appears as a pie slice, starts from the top of the circle.
Setting The Value
To provide text and numerical representation of the user’s actions, we’ll add <label>
and <output>
elements. The markup becomes:
<label for="utilslider">Task CPU Utilization</label>
<div id="readout">
<svg viewBox="0 0 100 100">
<circle r="25" cx="50" cy="50" id="pie" />
</svg>
<output for="utilslider">15%</output>
</div>
<input type="range" min="0" max="100" value="15" id="utilslider">
I’ll leave the remaining styles for these elements to the CodePen demo; for now, we’ll concentrate on the JavaScript added to the bottom of the page. First, we’ll grab hold of the various object references and variables that we’ll need:
var utilslider = document.getElementById("utilslider"),
circle = document.getElementById("pie"),
radius = parseInt(circle.getAttribute('r'), 2),
circumference = 2 * radius * Math.PI,
percentDisplay = document.querySelector("#readout output");
We don’t want to hard-code the radius or circumference of the circle element into our JavaScript, in case it changes in our markup; instead, we’ll read it (converting the radius
from a text string to a floating point number) and calculate the circumference of the circle from that value.
We want to call a function to set the correct “slice” of the circle
element from the current value
of utilsider
. The same function will also be used to set the pie slice of the circle from the utilsider
initial value:
utilslider.addEventListener("input",
function() { pieSlicer(); }
)
pieSlicer();
The pieSlicer
function itself:
function pieSlicer() {
var percentValue = (utilslider.value / 100) * circumference;
pie.style.strokeDasharray = percentValue + " " + circumference;
percentDisplay.innerHTML = utilslider.value + "%";
}
Changing Colors
By default the pie slice color will remain whatever we set it to in the CSS. The easiest way to change this dynamically is to use HSL. A red color would be defined as hsl(0, 50%, 50%)
, with the intensity of the color determined by the middle value. If we can make that value respond to the value of utilslider
in our function:
pie.style.stroke = "hsl(0 ," + utilslider.value + "%, 50%)";
…the color of the slice will grow from black to pure red as the value of the slider increases. It’s also possible to shift the pie slice through a range of colors, which I’ll cover in a future article.
Transitioned Changes
Tony Downey had the excellent suggestion of adding a transition to the stroke, which would mean that it would respond to clicks on the range
input over time, rather than “jumping” to new values. In our CSS:
#pie {
transition:stroke-dasharray 2s;
}
This also provides the rather nice effect of the pie slice “catching up” to any changes made in the utilslider
.
This is, of course, only the visual radial display of a numerical value; in some cases, the UI and display are best integrated in the same element, which I will be looking at next.
Enjoy this piece? I invite you to follow me at twitter.com/dudleystorey to learn more.
Check out the CodePen demo for this article at https://codepen.io/dudleystorey/pen/LNdaZX