1. Web Design
  2. HTML/CSS
  3. Animation

Crea una animación de carga de página en JavaScript con GSAP

En el tutorial de hoy, aprenderemos a crear una animación de carga de página con GSAP, una de las bibliotecas para animaciones con JavaScript más predominantes y populares.
Scroll to top

Spanish (Español) translation by Carlos (you can also view the original English article)

Final product imageFinal product imageFinal product image
What You'll Be Creating

En el tutorial de hoy, aprenderemos a crear una animación de carga de página con GSAP, una de las bibliotecas para animaciones con JavaScript más predominantes y populares.

Para comprender mejor lo que vamos a crear, revisa la página de demostración. Asegúrate de hacer clic en los enlaces del menú para repetir la animación.

Este tipo de animaciones funcionan muy bien en combinación con bibliotecas de transición de página como barba.js y Highway.js.

Demostración de la animación de la página

Para este tutorial, nuestra demostración no se alojará en CodePen. Como necesitamos diferentes páginas para mostrar la animación, decidí que es mejor alojarla en GitHub. Esta es la estructura del proyecto:

1
panels-animation/
2
├── about.html
3
├── contact.html
4
├── index.html
5
├── main.css
6
└── main.js

Antes de continuar, cabe destacar que la inspiración para esta demostración se tomó del sitio web de Pure Cinema.

1. Empieza con el marcado de la página

Vamos a describir el marcado de la página index.html. Esta será similar a las otras páginas.

En su interior, colocaremos:

  • Una típica cabecera de página
  • Los paneles que se encargarán de dividir la pantalla en seis partes iguales
  • El elemento main donde se alojará el contenido principal de la página.

Asimismo, importaremos:

Considerando todo lo anterior, aquí está el marcado asociado:

1
<!doctype html>
2
<html lang="en">
3
  <head>
4
    <meta charset="utf-8">
5
    <meta name="viewport" content="width=device-width, initial-scale=1">
6
    <link rel="preconnect" href="https://fonts.gstatic.com">
7
    <link rel="stylesheet" href="https://fonts.googleapis.com/css2?family=Montserrat:wght@400;700&display=swap">
8
    <link rel="stylesheet" href="main.css">
9
    <title>Simple Page Loading Animation With GSAP</title>
10
  </head>
11
  
12
  <body>
13
    <header class="page-header">
14
      <nav>
15
        <ul>
16
          <li>
17
            <a href="index.html">Home</a>
18
          </li>
19
          <!-- more list items -->
20
        </ul>
21
      </nav>
22
    </header>
23
    
24
    <ul class="panels">
25
      <li class="panel"></li>
26
      <li class="panel"></li>
27
      <li class="panel"></li>
28
      <li class="panel"></li>
29
      <li class="panel"></li>
30
      <li class="panel"></li>
31
    </ul>
32
    
33
    <main class="page-main">
34
      <div>
35
        <h1>Home Page</h1>
36
        <!-- put more content here -->
37
      </div>
38
    </main>
39
    
40
    <script src="https://unpkg.co/gsap@3/dist/gsap.min.js"></script>
41
    <script src="main.js"></script>   
42
  </body>
43
</html>

2. Define algunos estilos básicos

Como es habitual, continuaremos con algunas variables de CSS y restableceremos los estilos:

1
:root {
2
    --panel-width: calc(100% / 6);
3
    --darkblue: #02020c;
4
    --white: #fff;
5
    --lightgray: #fafafb;
6
}
7
8
* {
9
    padding: 0;
10
    margin: 0;
11
    box-sizing: border-box;
12
}
13
14
ul {
15
    list-style: none;
16
}
17
18
a {
19
    color: inherit;
20
    text-decoration: none;
21
}
22
23
h1 {
24
    font-size: 3rem;
25
}
26
27
body {
28
    height: 100vh;
29
    font-family: "Montserrat", sans-serif;
30
    color: var(--white);
31
    overflow: hidden;
32
}

Se deben tener en cuenta dos cosas:

  • La variable panel-width determinará el ancho del panel.
  • La altura de la página será igual a la altura de la ventana gráfica.

3. Especifica los estilos principales

Ahora concentrémonos en los estilos principales. Dejaremos de lado los estilos de la cabecera ya que no tienen ninguna importancia.

Contenedor de panel

El contenedor del panel será un elemento a pantalla completa y posicionado fijo. Estará centrado de manera horizontal y sesgado hasta cierto punto. Además, debido a su distorsión, su ancho siempre sobrepasará y dependerá del ancho de la ventana gráfica. Como vamos a animar su propiedad clip-path más adelante, definiremos un valor predeterminado para esta propiedad que indicará la visibilidad completa del elemento.

Los paneles

Los paneles serán elementos posicionados de manera absoluta, y su ancho (width) y los valores de la propiedad left dependerán de la variable panel-width. Aclarado el punto, el valor de left para el primer panel será 0, para el segundo alrededor del 16.666%, para el tercero alrededor del 33.333%, y así sucesivamente. Además, al principio, serán invisibles. Cuando se cargue la página, aparecerán con una animación de deslizamiento de arriba hacia abajo o bien de abajo hacia arriba.

Elemento principal

El elemento main será a pantalla completa con contenido centrado horizontal y verticalmente. En este caso, contendrá solamente un título, pero si usas contenido cuya altura exceda la altura de la ventana gráfica, aparecerá una barra de desplazamiento. Inicialmente, solo el 20% será visible. Cuando se cargue la página, aparecerá todo el elemento.

Aquí están los estilos relevantes:

1
.panels {
2
    position: fixed;
3
    top: 0;
4
    left: 50%;
5
    width: 180vw;
6
    height: 100%;
7
    transform: translateX(-50%) skewX(-35deg);
8
    clip-path: circle(100%);
9
    z-index: 1;
10
    background: var(--lightgray);
11
}
12
13
.panels .panel {
14
    position: absolute;
15
    top: 0;
16
    left: 0;
17
    bottom: 0;
18
    width: var(--panel-width);
19
    transform: scaleY(0);
20
    transform-origin: top;
21
    background: var(--darkblue);
22
}
23
24
.panels .panel:nth-child(even) {
25
    transform-origin: bottom;
26
}
27
28
.panels .panel:nth-child(2) {
29
    left: calc(var(--panel-width) - 1px);
30
}
31
32
.panels .panel:nth-child(3) {
33
    left: calc(calc(var(--panel-width) * 2) - 2px);
34
}
35
36
.panels .panel:nth-child(4) {
37
    left: calc(calc(var(--panel-width) * 3) - 4px);
38
}
39
40
.panels .panel:nth-child(5) {
41
    left: calc(calc(var(--panel-width) * 4) - 5px);
42
}
43
44
.panels .panel:nth-child(6) {
45
    left: calc(calc(var(--panel-width) * 5) - 6px);
46
}
47
48
.page-main {
49
    display: flex;
50
    height: 100%;
51
    padding: 100px 15px;
52
    clip-path: circle(20%);
53
    overflow-y: auto;
54
    background: var(--darkblue);
55
}
56
57
.page-main > div {
58
    text-align: center;
59
    margin: auto;
60
}
61
62
@media (max-width: 1024px) {
63
    .panels {
64
        width: 200vw;
65
    }
66
}
67
68
@media screen and (max-width: 600px) {
69
    .panels {
70
        width: 235vw;
71
    }
72
}

Nota: Si revisas el valor de left de los paneles, notarás que hay una función exterior calc(). Su función es hacer que los paneles se superpongan un poco, y así evitar los bordes blancos entre los paneles adyacentes. Los valores restados (por ejemplo, -2px) provienen de prueba y error.

The white lines that appear between the panelsThe white lines that appear between the panelsThe white lines that appear between the panels

4. Activa las animaciones

Ahora pongamos a GSAP en el juego.

Para crear las animaciones, aprovecharemos Timeline, una herramienta de animación que nos brindará la posibilidad de crear una secuencia de interpolaciones/animaciones.

Primero, vamos a crear una línea de tiempo y estableceremos su estado como paused. De esta manera, las animaciones no se reproducirán de forma predeterminada. Cuando la página se haya cargado completamente, se reproducirán gracias a su método play().

En su interior, añadiremos una secuencia de interpolaciones utilizando su método to(). Este método puede recibir los siguientes parámetros:

  • El elemento del DOM que queremos animar.
  • Un objeto que contendrá las propiedades que deben ser animadas junto con sus respectivos valores finales. Este objeto puede recibir propiedades adicionales como duration, que controlará la duración de la animación en segundos.
  • La posición de la interpolación en la línea de tiempo. En otras palabras, cuándo debería ejecutarse esta interpolación. Si no le especificamos un valor, se añadirá al final de la línea de tiempo.

En nuestro caso, la cadena de animación será de la siguiente forma:

  1. Aparecerán el primer y último panel. La duración de la animación será de 1 segundo.
  2. Los otros paneles empezarán a aparecer 0.5 segundos antes del final de la línea de tiempo. La duración de la animación será de 0.5 segundos. Observa que omitimos la propiedad duration ya que su valor predeterminado es 0.5.
  3. Todos los paneles empezarán a desaparecer con un retraso de 0.05 segundos entre ellos. Dicho esto, el primer panel se iniciará, después el segundo luego de un retraso de 0.05 segundos continuará, después el tercero después de un retraso de 0.1 segundos, y así sucesivamente. La duración de la animación será de 0.3 segundos.
  4. El contenedor del panel desaparecerá mediante la propiedad clip-path. La duración de la animación será de 1 segundo.
  5. Por último, el elemento main de la página empezará a incrementar a través de la propiedad clip-path 0.3 segundos antes del final de la línea de tiempo. La duración de la animación también será de 1 segundo.

Deájame explicarte un aspecto complejo sobre las dos primeras interpolaciones.

La animación de deslizamiento de todos los paneles finalizará al mismo tiempo. Recuerda que durará 1 segundo para el primer y último panel, mientras que 0.5 segundos para los demás, pero en este caso emepezará 0.5 segundos antes. Este comportamiento no está suficientemente claro en nuestra demostración porque los estilos ocultan una parte del primer y último panel. Pero, si eliminamos la inclinación del contenedor del panel y le asignamos width: 100vw, podremos probarlo. Analiza esta demostración en CodePen.

Aquí está el código JavaScript asociado:

1
const tl = gsap.timeline({ paused: true });
2
3
tl.to(".panels .panel:first-child, .panels .panel:last-child", {
4
  scaleY: 1,
5
  duration: 1
6
})
7
  .to(
8
    ".panels .panel:not(:first-child):not(:last-child)",
9
    { scaleY: 1 },
10
    "-=0.5"
11
  )
12
  .to(".panels .panel", {
13
    scaleY: 0,
14
    duration: 0.3,
15
    stagger: 0.05
16
  })
17
  .to(".panels", {
18
    clipPath: "circle(0%)",
19
    skewX: 0,
20
    duration: 1
21
  })
22
  .to(
23
    ".page-main",
24
    {
25
      clipPath: "circle(100%)",
26
      duration: 1
27
    },
28
    "-=0.3"
29
  );
30
31
window.addEventListener("load", function () {
32
  tl.play();
33
});

Naturalmente, puedes usar la consola del navegador para revisar el rastro que deja el plugin.

GSAP footprintGSAP footprintGSAP footprint

Extra

Podemos utilizar el método duration() de la línea de tiempo para recuperar su duración. En nuestro caso, serán 3.25 segundos. Conozcamos el origen de este número:

  • 1 segundo de la primera interpolación
  • 0 segundos de la segunda, ya que terminará al mismo tiempo que la primera.
  • 0.55 segundos de la tercera, ya que el último panel empezará a desaparecer con un retraso de 0.25 segundos (0.05 segundos x 5). Asi que, el tiempo total saldrá de la suma del retraso y la duración de la animación (es decir, 0.25 + 0.3).
  • 1 segundo de la cuarta
  • 0.7 segundos de la última, ya que comenzará 0.3 segundos antes del final de la línea de tiempo.

Conclusión

¡Felicidades, amigos! Hemos logrado crear una atractiva animación de carga de página aprovechando la biblioteca de animaciones GSAP. Este proyecto no es más que una pequeñísima muestra de las capacidades de GSAP. No dudes en ampliarla de acuerdo a tus necesidades (por ejemplo, hacer que las interpolaciones sean más rápidas) y así ampliar tus conocimientos sobre GSAP. Aún mejor, trata de incorporarla en una biblioteca de transiciones de página como barba.js.

Si deseas más tutoriales sobre GSAP o tutoriales relacionados con otras bibliotecas de animación, háznoslo saber a través de las redes sociales.

Como siempre, ¡muchas gracias por leer!