En este tutorial te explicaré paso a paso como implementar un contador regresivo (ó en ingles Countdown Timer) en HTML, con SVG para dibujar las formas, con CSS para los estilos visuales y con JavaScript para realizar el conteo regresivo y las animaciones. Nota: No usaremos ninguna librería extra así que nuestro código será rápido y ligero. Empecemos!

No olvides seguirnos en nuestras redes sociales y nuestra canal de Youtube donde publicamos contenidos relacionados con el desarrollo frontend. Síguenos, dale like y suscríbete, nos ayudas a seguir publicando más contenidos que pueden ayudar a muchos!

Mockup / Wireframe

Mockup / Wireframe de un contador de cuenta regresiva usando SVG, CSS y JavaScript

Indice:

Video de codificación

Estructura HTML

La parte de HTML, tal como se ve en el mockup, tendrá un texto de cabecera y los círculos, y dentro de estos una etiqueta para los días, horas, minutos y segundos respectivamente.

Para efectuar el efecto de los circulos en teoría funciona así:

Un div en forma de circulo con un fondo transparente y un borde grueso.

El siguiente elemento será un SVG, y dentro una forma de circulo. Usamos aquí SVG por que a este circulo le crearemos el efecto de desplazamiento tipo ajuga de reloj.

Finalmente al frente un etiqueta con el texto del conteo regresivo, sea dia, hora, minutos o segundo.

index.html

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Countdown timer</title>

    <link rel="stylesheet" href="css/style.css">
</head>
<body>

    <section class="container">

        <h1>Comming soon</h1>
        <p>Save the date</p>

        <div class="charts">

            <div class="chart">
                <!-- un circulo inicial de fondo -->
                <div class="circle center-abs"></div>

                <!-- area para SVG -->

                <svg class="center-abs" width="150" height="150">
                    <!-- un segundo circulo en SVG con su ubicacion en coordenadas x,y y el radio de expansion -->
                    <circle class="outer" cx="75" cy="75" r="70" />
                </svg>

                <!-- etiqueta para el contador, en este caso el dia -->
                <span class="text center-abs" id="days"></span>
                <h3>Days</h3>
            </div>

            <div class="chart">
                <div class="circle center-abs"></div>
                <svg class="center-abs" width="150" height="150">
                    <circle class="outer" cx="75" cy="75" r="70" />
                </svg>
                <span class="text center-abs" id="hours"></span>
                <h3>Hours</h3>
            </div>

            <div class="chart">
                <div class="circle center-abs"></div>
                <svg class="center-abs" width="150" height="150">
                    <circle class="outer" cx="75" cy="75" r="70" />
                </svg>
                <span class="text center-abs" id="minutes"></span>
                <h3>Minutes</h3>
            </div>

            <div class="chart">
                <div class="circle center-abs"></div>
                <svg class="center-abs" width="150" height="150">
                    <circle class="outer" cx="75" cy="75" r="70" />
                </svg>
                <span class="text center-abs" id="seconds"></span>
                <h3>Seconds</h3>
            </div>
        </div>

    </section>

    
    <script src="js/script.js"></script>
</body>
</html>

Hoja de estilos

Ahora dar estilo visual a nuestro HTML. Fijate bien sobre todo la parte de los circulos SVG.

Explicaremos algunos atributos para el circulo SVG:

  • stroke-width: define el grosor del contorno del circulo.
  • stroke-dasharray: define el patrón de rayas y espacios utilizados para pintar el contorno de la forma. A mayor numero más poblado creando un efecto de contorno lleno.
  • stroke-dashoffset: define un desplazamiento del patrón de rayas, esta en contraparte con stroke-dasharray. Ejemplo: si stroke-dasharray tiene un valor de 300, y stroke-dashoffset un valor de 150, creara un circulo con un borde solo a la mitad.

style.css

@import url(https://fonts.googleapis.com/css?family=Lato:300,400,700);

*{
    font-family: 'Lato', 'sans-serif';
    padding: 0;
    margin: 0;
    box-sizing: border-box;
}
body{
    background-image: url(../images/bg.jpg);
    background-size: cover;
    background-repeat: no-repeat;
    min-height: 100vh;
}
.container{
    max-width: 900px;
    margin:60px auto;
}
.container h1,
.container p{
    color:#fff;
    text-align: center;
    padding: 20px 0;
}

.container h1{
    font-size: 4em;
}
.container p{
    font-size: 2em;
}
.center-abs{
    top:40%;
    left:50%;
    transform:translate(-50%, -50%);
    position: absolute;
}

.charts{
    display: flex;
}
.chart{
    position: relative;
    width:25%;
    height: 260px;
}
.chart h3{
    color:#fff;
    font-size: 1.8em;
    position: absolute;
    bottom:10px;
    text-align: center;
    width: 100%;
    text-shadow: 1px 1px 10px #000;
}
.chart svg{
    z-index: 2;
}

/* DIV CIRCULO DE FONDO */
.circle{
    width: 150px;
    height:150px;
    border-radius: 50%;
    z-index:1;
    border:10px #baeaff solid;
}
.text{
    z-index: 3;
    text-align: center;
    width: 100%;
    font-size: 3.8em;
    font-weight: 700;
    color:#0096da
}
/* SVG CIRCULO */
.outer{
    fill:transparent;
    stroke: #0096da;
    stroke-width: 10; /* grosor del borde */
    stroke-dasharray: 439; /* numero de relleno del borde, se usa el mismo valor en el script.js */
    transform: rotate(270deg) translate(-150px, 0); 
    /* giramos el circulo para que su apertura sea en la parte superior 
    y ajustamos su posicion
    */
}

Funciones JavaScript

Para el conteo regresivo existe muchas funciones googleando, encontré una muy práctica y se acomoda a nuestro proyecto es de w3schools.

La vamos a modificar en primer lugar, establecido una fecha distinta a gusto de ustedes (la fecha es en formato ingles).

Además vamos añadir una función extra que capture el valor de la cuenta regresiva y establezca la animación de los círculos.

script.js

// Set the date we're counting down to
// Establecer la fecha aqui, en formato ingles
var countDownDate = new Date("Oct 20, 2021 14:00:00").getTime();

// Update the count down every 1 second
var x = setInterval(function() {

  // Get today's date and time
  var now = new Date().getTime();

  // Find the distance between now and the count down date
  var distance = countDownDate - now;

  // Time calculations for days, hours, minutes and seconds
  var days = Math.floor(distance / (1000 * 60 * 60 * 24));
  var hours = Math.floor((distance % (1000 * 60 * 60 * 24)) / (1000 * 60 * 60));
  var minutes = Math.floor((distance % (1000 * 60 * 60)) / (1000 * 60));
  var seconds = Math.floor((distance % (1000 * 60)) / 1000);

  // Display the result in the element with id="demo"
  document.getElementById("days").innerHTML =  days;
  document.getElementById("hours").innerHTML =  hours;
  document.getElementById("minutes").innerHTML =  minutes;
  document.getElementById("seconds").innerHTML =  seconds;

  // aqui añadimos nuestra funcion, para que se segun 
  // el conteo regresivo anime los circulos
  effectCircle(days, hours, minutes, seconds);

}, 1000);

effectCircle = function(d, h, m, s){
    // valores maximos
    const max_sec = 60;
    const max_min = 60;
    const max_hour = 24;
    const max_day = 30;

    // valor del relleno del contorno
    // ojo es el mismo establecido en la hoja de estilos
    const strokeDashoffset = 439; 

    var circleSVG = document.getElementsByClassName('outer');

    // circulo dias
    var valPerDay = strokeDashoffset / max_day; // el valor de cada periodo segun la longitud del circulo
    var size = strokeDashoffset - (valPerDay * d); // restamos el valor del periodo a la longitud del circulo
    circleSVG[0].style.strokeDashoffset = size; // establemos nuevo valor al la longitud del circulo

    // circulo horas
    var valPerHour = strokeDashoffset / max_hour;
    var size = strokeDashoffset - (valPerHour * h);
    circleSVG[1].style.strokeDashoffset = size;

    // circulo minutos
    var valPerMin = strokeDashoffset / max_min;
    var size = strokeDashoffset - (valPerMin * m);
    circleSVG[2].style.strokeDashoffset = size;

    // circulo segundos
    var valPerSecond = strokeDashoffset / max_sec;
    var size = strokeDashoffset - (valPerSecond * s);
    circleSVG[3].style.strokeDashoffset = size;

}

Ahora podremos ejecutar y veremos funcionando nuestro contador, mientras que el borde de los círculos crean un efecto de desplazamiento, similar a: contrario a las ajugas de reloj.

Dudas, preguntas, comentarios me lo hacen llegar, aquí en el post o el video en Youtube espero poder ayudarles.

Descarga el código desde nuestro github. Espero le sirva esta herramienta.