Barra de progreso animada con un SVG circular

Fórmula para crear una barra de progreso circular animada usando una imagen SVG con CSS.

Progreso animado circular de una imagen SVG

Para indicar el progreso de finalización de una tarea, a menudo se utilizan elementos visuales para mejorar la experiencia del usuario y proporcionar una representación gráfica clara y rápida del estado de su avance.

Quizá habrás visto barras de progreso lineales, que son adecuadas para muchos casos de uso, pero los círculos de progreso círculares pueden ser más llamativos al ofrecer una sensación más dinámica y estéticamente agradable. Pero su implementación es algo más complicada. A continuación intentaremos desgranar todos los pasos para crear una imagen en formato vectorial SVG (Scalable Vector Graphic) y animarla con sólo CSS primero y después con algo de Javascript.

Círculo SVG

Es posible crear este componente sólo con CSS, pero hacerlo con SVG ofrece algunas ventajas. Desde tener más control sobre su apariencia, hasta hacer más sencilla la animación como veremos más adelante.

La imagen del círculo estará compuesta en realidad por dos círculos. Uno representará la pista (o el fondo), el segundo mostrará el progreso actual. Aquí tenemos el código de la imagen:

<svg height="100" width="100">
  <circle cx="50" cy="50" r="40" stroke="#eee" stroke-width="12" fill="transparent"  />
  <circle cx="50" cy="50" r="40" stroke="#00f" stroke-width="12" fill="transparent" class="progress"  />
</svg>

Apariencia y estilos

Para mostrar la imagen deberemos aplicar algunas transformaciones y animaciones. Podemos usar el siguiente CSS para ello.

svg {
  transform: rotate(-90deg);
}

.progress {
  stroke-dasharray: 251.327;
  stroke-dashoffset: 251.327;
  animation: progress 1s ease-in forwards;
}

@keyframes progress {
  to {
    stroke-dashoffset: 0;
  }
}

Primero aplicaremos una rotación de -90 grados para que la animación empieze desde la parte superior del círculo.

Luego nos valdremos de las propiedades stroke-dasharray para partir la linea en segmentos y stroke-dashoffset para establecer la ubicación a lo largo de una ruta SVG donde comenzará el patrón de guiones del trazo. Podemos usar las reglas animation y @keyframes en CSS para controlar los pasos de la animación.

En este primer caso sólo usaremos CSS debido a que no podemos modificar la propiedad de animación @keyframes una vez definida. Nos puede ser útil en casos donde sabemos el porcentaje total de progreso antes de renderizar la página.

Este es el resultado:

Calcular progreso con stroke-dasharray y stroke-dashoffset

Para determinar los valores de stroke-dasharray y stroke-dashoffset basta con aplicar la siguiente fórmula:

circunferencia =  2 * Math.PI * radio 

dashOffset = circunferencia * ((100 - progreso) / 100)

Con los tamaños de los círculos del ejemplo anterior sería:

circunferencia =  2 * 3.1415 * 40 = 251.327

dashOffset = 251.327 * ((100 - 100) / 100) = 0 // 100% del progreso

Hay casos en los que nos será útil modificar la variable de progreso de forma automática. Para ello debemos hacer algunos cambios en los estilos y usar Javascript como veremos en el siguiente punto.

Modificar el valor del porcentaje dinámicamente

Si queremos variar el valor del porcentaje de progreso a mostrar de forma dinámica, no es conveniente usar las reglas animation ni @keyframes, y en cambio, usar la regla transition. Luego en Javascript podemos modificar la propiedad stroke-dashoffset, y la barra de progreso se moverá a la posición marcada de una forma fluida. Veamos cómo funciona:

svg {
  transform: rotate(-90deg);
}

.progress {
  stroke-dasharray: 251.327;
  stroke-dashoffset: 251.327;
  transition: stroke-dashoffset 1s ease-in-out;
}

Con javascript añadimos algunas segundos de retraso entre cada modificación:

const element = document.querySelector('.progress')
setTimeout(() => {
  element.style.strokeDashoffset = 0
}, 0)
setTimeout(() => {
  element.style.strokeDashoffset = 100
}, 1500)
setTimeout(() => {
  element.style.strokeDashoffset = 100
}, 3000)