Progress Bar en forma de Arco

Las progress bar habitualmente consisten en una barra de color que va aumentando o disminuyendo de longitud mientras ocurre algo: cargar un fichero, recorrer un form multistep... En esta aplicación te presento lo que podriamos llamar una progress arc en lugar de una barra creciente se usa un arco de circunferencia, o un sector circular, para ilustrar el progreso de una tarea.

Solución

<script>

/*Progress bar con forma de arco o sector circular*/

/*Convertir polres (radio, ángulo) a cartesianas (x, y)*/

function polarToCartesian(centerX, centerY, radius, angleInDegrees) {

var angleInRadians = (angleInDegrees-90) * Math.PI / 180.0;

return {

x: centerX + (radius * Math.cos(angleInRadians)),

y: centerY + (radius * Math.sin(angleInRadians))

};

}

/*Consturir el path del arco*/

function describeArc(x, y, radius, startAngle, endAngle){

var start = polarToCartesian(x, y, radius, endAngle);

var end = polarToCartesian(x, y, radius, startAngle);

var largeArcFlag = endAngle - startAngle <= 180 ? "0" : "1";

var d = [

"M", start.x, start.y,

"A", radius, radius, 0, largeArcFlag, 0, end.x, end.y

].join(" ");

return d;

}

/*Clase para un arco, los datos se obtienen del elemento del DOM con id = ident */

class ProgressArc{

    constructor(ident){

    let container = document.getElementById(ident);

    this.end = parseInt(container.dataset.value);

    this.end = (this.end > 100)?100:this.end;

    this.radio = parseInt(window.getComputedStyle(container).height)/2;

    this.text = container.getElementsByTagName('div')[0];

    this.arc = container.getElementsByTagName('svg')[0];

    this.actual = 0

    this.arc.style.height = this.radio*2+'px';

    }

/*Animación */

play(){

    this.timer = setInterval(this.draw.bind(this), 25)

}

/*Dibuja el arco*/

draw(){

    let path = this.arc.children[0]

    let angle = 0;

    this.actual++;

    angle = this.actual*3.599;

    if (this.actual >= this.end){

    clearInterval(this.timer)

    path.setAttribute("d", describeArc(this.radio, this.radio, this.radio, 0, angle));

    }

else

    path.setAttribute("d", describeArc(this.radio, this.radio, this.radio, 0, angle));

    this.text.innerText = this.actual+"%";

    }

}

/*Crear los objetos para cada progressArc de la página*/

    window.onload = function() {

    var arco = new ProgressArc('arco');

    arco.play();

}

</script>

<style>

*{

  margin: 0;

  padding: 0;

  font-family: 'Arial', sans-serif;

}

/*Para centrar en la página*/

html, body{

   display:grid;

   height:100%;

   text-align: center;

   place-items: center;

   background: #dde6f0;

}

.progressArc{

  position: relative;

  display: flex;

  align-content: center;

  align-items: center;

  justify-content: center;

  text-align: center;

  height: 250px;

  width: 250px;

}

.progressArc div{

z-index:1;

}

.progressArc svg{

  background: rgb(134, 237, 81);

  border-radius: 50%;

  aspect-ratio: 1 / 1;

  height: 100%;

  position: absolute;

  top: 0;

  left: 0;

  z-index:0;

}

</style>

<body>

<div class="progressArc" id="arco" data-value = "75">

<div></div>

<svg><path fill="none" stroke="#446688" stroke-width="20"/></svg>

</div>

</body>

Explicación

Las progress bar son habituales para indicar el grado en que se ha alcanzado un objetivo, o el porcentaje de progreso de una tarea.

Esta app implementa una progress Arc, o sea que en lugar de una barra se mostrará un arco de circunferencia creciente, o un sector circular.

La aplicación se base en un gráfico SVG dinámico, es decir que se va a modificar mediante un script. Concreatamente apra dibujar un arco.

El elemento SVG contiene el color del trazo en el atributo stroke y el grosor del mismo, en stroke-width. Si este ancho se coloca igual al ancho del contenedor del SVG tendremos un sector circular en lugar de un arco.

El elemento SVG se coloca en forma absoluta dentro de su contenedor para lograr una alineación adecuada.

En el body se debe poner un bloque div (el contenedor del SCG) que contendrá otro div para mostrar el avance de la barra en número (0,1%, 2%...)  y el SVG que sibuja el arco cambiante.

El contenedor tiens la clase progressArc y un dataset (data-valor) para indicar el porcentaje del círculo que se rellenará. La clase es la responsable de la dimensión son loas propiedades height width, y de la alineación del texto con el SVG (colocar el texto en el centro).

En el script se crea una clase desde la que se podrán crear uno o más Progress Arc. Está pensado para poder usar varios en una mismo bloque, por tanto con la misma rapidez y estilos.