15%

There are certain kinds of measurements, such as time, percentage, and angle, that are best displayed visually, often in an arc or circle. 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 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