JSFiddle - React, Tailwind, and code Playground

by Ed Bulikyan

HTML

<div class="columns">
	<div><div class="dial" data-width="80" data-lineWidth="2">12.2567567</div></div>
	<div><div class="dial blue" data-width="180" data-lineWidth="41">66</div></div>
	<div><div class="dial error" data-lineWidth="20" data-lineRound="true">33.6567567</div></div>
</div>
<div class="columns">
	<div><div class="dial" data-width="40" data-lineWidth="41">40.2567567</div></div>
	<div><div class="dial success" data-width="80">81.1567567</div></div>
</div>

SCSS

body {
	background-color: #2f323e;
	font-family: Consolas, monaco, monospace;
}
.columns {
	display: flex;
	align-items: center;
	> div {
		flex: 1;
		text-align: center;
		padding: 20px;
	}
}

.dial {
	border-color: #22262f;
	color: #689F38;
	display: inline-block;
	text-align: left;
	p {
		text-align:center;
		font-weight: bold;
		color: #fff;
		white-space: nowrap;
		position: relative;
		overflow: hidden;
		z-index: 1;
		margin: 0;
	}
	canvas {
		position: absolute;
	}
}
/* разные цветовые схемы */
.dial {
	&.error {
		color: #d46d71;
		p {
			color: #d46d71;
		}
	}
	&.success {
		color: #689F38;
		border-color: rgba(#d46d71, 0.4);
		p{
			color: #689F38;
		}
	}
	&.blue {
		color: #1390d4;
	}
}

JavaScript

$(function(){
	// Ищем все элементы с class="dial"
	var dials = $(".dial");
	// Перебираем все .dial и пихуем туда canvas с графиком.
	for (i=0; i < dials.length; i++){
		var width = (typeof $(dials[i]).attr("data-width") != 'undefined') ? Math.round($(dials[i]).attr("data-width")) : 80;
		var procent = (Number($(dials[i]).html()) > 0 && Number($(dials[i]).html()) < 100) ? Math.round(Number($(dials[i]).html()) * 10)/10 : 0;
		var lineWidth = (typeof $(dials[i]).attr("data-lineWidth") != 'undefined') ? Number($(dials[i]).attr("data-lineWidth")) : width / 10;
		var size = width+lineWidth;
		var lineRound = (typeof $(dials[i]).attr("data-lineRound") != 'undefined') ? true : false;
		var borderColor = $(dials[i]).css("border-color");
		var color = $(dials[i]).css("color");
		// Меняем размер .dial в зависимости от data-width="80"
		// Устанавливаем размер шрифта так что бы он вмещался в круг не задевая border
		$(dials[i]).css({"width": size + 'px', "height": size + 'px', "font-size": Math.floor((width-lineWidth) / 4) + 'px'});
		// Вставляем canvas такого же размера что и родитель.
		$(dials[i]).html('<canvas id="dial' + i + '" width="' + size + '" height="' + size + '"></canvas><p>' + procent + '%</p>');
		// Выравниваем текст по вертикали
		$("p", dials[i]).css({"line-height": size + 'px'});
		var canvas = document.getElementById("dial" + i);
    var context = canvas.getContext("2d");
		// считаем по формуле радианы
		var radian = 2*Math.PI*procent/100;
		// рисуем круг для фона
		context.arc(width/2+lineWidth/2, width/2+lineWidth/2, width/2, 0, 2*Math.PI, false);
		context.lineWidth = lineWidth;
		context.strokeStyle = borderColor;
		context.stroke();
		context.beginPath();
		// рисуем круг с процентами
		context.arc(width/2+lineWidth/2, width/2+lineWidth/2, width/2, 1.5 * Math.PI, radian+1.5 * Math.PI, false);
		context.strokeStyle = color;
		// Можно скруглить концы отрезка если передан параметр data-lineRound
		if (lineRound == true && lineWidth < width)...