1. Web Design
  2. SEO
  3. Performance

Mejora de Rendimiento: Cómo Cargar Imágenes Usando in-view.js

Scroll to top

Spanish (Español) translation by Rafael Chavarría (you can also view the original English article)

En este tutorial te mostraré cómo mejorar el rendimiento de tus páginas web usando in-view.js. Esta librería JavaScript reporta cuando algo aparece en la ventana de visualización y nos ayudará a cargar dinámicamente nuestras imágenes mientras sea necesario.

El Rendimiento Importa

El rendimiento de la web importa, especialmente si tu sitio web apunta a países en desarrollo en donde las conexiones son lentas y los planes de dato son caros. Unos pequeñas tareas básicas que comunmente emprendemos para mejorar el rendimiento de nuestros sitios web incluyen minificar archivos JavaScript y hojas de estilo, "comprimiendo" recursos y tamaños de imágenes, despues de lo que estamos prácticamente listos. ¿Pero lo estamos?

Tiempo de carga de página e inspector de línea de tiempo del navegador

El ejemplo de inspector de arriba muestra una página sencilla cargando 24 imágenes en una ventana de vista tamaño móvil en una velocidad regular 3-G. ¡Y como podemos ver, la carga de la página está completa en alrededor de once segundos! Esto es realmente lento dado que estamos lidiando con una simple página con nada más que unas cuantas imágenes y una hoja de estilo. Esta página todavía no está contaminada con anuncios, ni scripts de seguimiento los cuales usualmente añanden lastre adicional a la página.

También vale la pena tener en mente, que es meramente una emulación. Ni siquiera toma en cuenta la configuración del servidor, latencia y otros obstáculos técnicos. El rendimiento podría ser incluso peor en la realidad.

¿Así que como podemos mejorar el rendmiento de carga de la página?

Cuello de botella

Primero, tenemos un número de imágenes. La razón de que nuestra página cargue lento es porque todas las imágenes están inundando juntas al inicio de la carga de la página. Si echas un vistazo más de cerca a la imagen anterior, verás que esto no sucede en paralelo: un par de imágenes solo comienzan a cargar una vez que otras han terminado de hacerlo, lo cuál satura a la página en conjunto.

Si tenemos un gran número de imágenes en una sola página, podemos considerar cargar esas imágenes de manera asíncrona y solo cuando el usuario las necesite. Esto permite al navegador completar la carga de la página visible sin necesidad de esperar para que todas las imágenes se muestren, ultimadamente ahorrando al usuario ancho de banda.

Comenzando

Para seguir a la par, toma el index-starter.html del repositorio. También hay un css/styles-starter.css de acompañamiento que también puedes usar.

Para comenzar, necesitamos reemplazar la fuente de las imágenes con una imágen realmente pequeña, preferentemente codificada en base64 para evitar cualquier petición HTTP extra. Usamos esta imagen como placeholder antes de que entreguemos la imagen real. Dicho eso, debemos también almacenar la verdadera fuente de imagen en un atributo personalizado llamado data-src.

1
<figure class="post__image">
2
   <img src="data:image/gif;base64,R0lGODlhAQABAIAAAAAAAP///yH5BAEAAAAALAAAAAABAAEAAAIBRAA7" data-src="./images/image-24.jpg" alt="" width="800" height="554">
3
</figure>

Una vez que has hecho esto y refrescado la página, deberías encontrar que las imágenes están actualmente en blanco y sus dimensiones no son necesariamente las que tus imágenes finales deberían tener.

Chrome Browser Window with square blank imageChrome Browser Window with square blank imageChrome Browser Window with square blank image

Así que arreglemos los estilos.

Reteniendo la Proporción de Imagen

Las imágenes que queremos usar están establecidas en 800 por 550 pixeles. Dividiremos el alto de la imagen (800px) entre el ancho de la imagen (500px), y multiplicamos esto por 100%. Usa el resultado para establecer el padding top del pseudo elemento del contenedor de la imagen. Por último, necesitamos establecer la posición de la imagen a absolute y establecer la altura máxima a 100%, de manera que no reforzará la altura.

1
figure {
2
   position: relative;
3
}
4
figure img {
5
   top: 0;
6
   left: 0;
7
   position: absolute;
8
   max-height: 100%;
9
}
10
figure:before {
11
   padding-top: 69.25%; // ( 554 / 800 ) * 100%
12
}

En este punto, las dimensiones de la imagen deberían ser correctas. Sin embargo, la funte real de la imagen aún reside en un atributo personalizado así que el navegador no puede traer ninguna imagen aún.

La imagen está en una proporción apropiadas, pero la imagen aún está cargada.

Nuestro siguiente paso será agregar un poco de JavaScript que cargará la imagen.

Cargando La Imagen

Primeramente, necesitamos cargar in-view.js a la página. Como se mencionó, esta ligera librería (que no depende de jQuery o una librería base como Waypoints) detecta si un elemento está dentro de la ventana de visualización del navegador.

Ahora crea un archivo JavaScript en donde escribiremos nuestro JavaScript y lo cargaremos después de in-view.js, como sigue:

1
<script src="./js/in-view.min.js"></script>
2
<script src="./js/scripts.js"></script>

Métodos y Funciones

La librería in-view.js expone la función inView() la cuál toma un selector como el argumento. En este caso, pasaremos el elemento figure; el elemento que envuelve a las imágenes. La razón de que seleccionemos el elemento de envoltura es porque vamos a agregar un par de clases para relizar transiciones de estilo--esto es más fácil cuando la clase está en el elemento envoltorio en vez de la imagen misma, de ahí:

1
inView('figure')

Después, usamos el método .on() para enlazar el elemento con el evento enter para revisar si el elemento está dentro de la ventana de visualización. Adicionalmente, in-view.js también expone el evento exit el cuál hace lo contrario; este detecta cuando el elemento está fuera de la visualización.

1
inView( 'figure' ).on( 'enter', function( figure ) {
2
3
     var img = figure.querySelector( 'img' ); // 1

4
5
     if (  'undefined' !== typeof img.dataset.src ) { // 2

6
7
         figure.classList.add( 'is-loading' ); // 3

8
9
         // 4

10
         newImg = new Image();
11
         newImg.src = img.dataset.src;
12
13
         newImg.addEventListener( 'load', function() {
14
15
            figure.innerHTML = ''; // 5

16
            figure.appendChild( this );
17
18
            // 6

19
            setTimeout( function() {
20
               figure.classList.remove( 'is-loading' );
21
               figure.classList.add( 'is-loaded' );
22
            }, 300 );
23
         } );
24
     }
25
} );

El evento enter disparará una función, la cual hará lo siguiente:

  1. Selecciona la imagen dentro de figure.
  2. Se asegura de que tiene el atriburo data-src.
  3. Agrega is-loading al envoltorio, figure, element.
  4. Carga una nueva imagen con la fuente traída del atributo data-src.
  5. Una vez que se carga agrega la imagen al contenedor.
  6. Y por último, reemplaza la clase is-loading con la clase is-loaded.

Queremos Ser Cargados

Como puedes ver del código de arriba, hemos introducido dos nuevas clases is-loading y is-loaded. Usamos la clase is-loading para agregar una animación de spinner mientras la imagen está cargando. Cuando usamos la clase is-loaded, como el nombre indica, agrega el efecto de transición a la imagen cuando la imagen ha sido completamente cargada.

1
figure.is-loaded img {
2
   animation: fadeIn 0.38s linear 1s forwards;
3
}
4
figure.is-loading {
5
   position: relative;
6
}
7
figure.is-loading:after {
8
   content: '';
9
   display: block;
10
   color: #ddd;
11
   font-size: 30px;
12
   text-indent: -9999em;
13
   overflow: hidden;
14
   width: 1em;
15
   height: 1em;
16
   border-radius: 50%;
17
   margin: auto;
18
   position: absolute;
19
   top: 50%;
20
   left: 50%;
21
   margin-left: -0.5em;
22
   margin-top: -0.5em;
23
   transform: translateZ(0);
24
   animation: loading 1.7s infinite ease;
25
}
26
@keyframes loading {
27
   0% {
28
     transform: rotate(0deg);
29
     box-shadow: 0 -0.83em 0 -0.4em, 0 -0.83em 0 -0.42em, 0 -0.83em 0 -0.44em, 0 -0.83em 0 -0.46em, 0 -0.83em 0 -0.477em;
30
   }
31
32
   5%,
33
   95% {
34
     box-shadow: 0 -0.83em 0 -0.4em, 0 -0.83em 0 -0.42em, 0 -0.83em 0 -0.44em, 0 -0.83em 0 -0.46em, 0 -0.83em 0 -0.477em;
35
   }
36
   10%,
37
   59% {
38
     box-shadow: 0 -0.83em 0 -0.4em, -0.087em -0.825em 0 -0.42em, -0.173em -0.812em 0 -0.44em, -0.256em -0.789em 0 -0.46em, -0.297em -0.775em 0 -0.477em;
39
   }
40
   20% {
41
     box-shadow: 0 -0.83em 0 -0.4em, -0.338em -0.758em 0 -0.42em, -0.555em -0.617em 0 -0.44em, -0.671em -0.488em 0 -0.46em, -0.749em -0.34em 0 -0.477em;
42
   }
43
   38% {
44
     box-shadow: 0 -0.83em 0 -0.4em, -0.377em -0.74em 0 -0.42em, -0.645em -0.522em 0 -0.44em, -0.775em -0.297em 0 -0.46em, -0.82em -0.09em 0 -0.477em;
45
   }
46
   100% {
47
     transform: rotate(360deg);
48
     box-shadow: 0 -0.83em 0 -0.4em, 0 -0.83em 0 -0.42em, 0 -0.83em 0 -0.44em, 0 -0.83em 0 -0.46em, 0 -0.83em 0 -0.477em;
49
   }
50
}
51
@keyframes fadeIn {
52
    0% {
53
        opacity: 0;
54
    }
55
56
    100% {
57
        opacity: 1;
58
    }
59
}

Retroceso

Espera lo mejor, pero planea para lo peor. Así que solo en caso de que JavaScript esté de alguna manera deshabilitado (un caso raro, pero perfectamente posible) necesitamos asegurarnos de que la imagen aún se mostrará. Usa el elemento <noscript> con la fuente de la imagen inmediatamente apuntando a la fuente real de la imagen.

1
<figure>
2
   <img src="data:image/gif;base64,R0lGODlhAQABAIAAAAAAAP///yH5BAEAAAAALAAAAAABAAEAAAIBRAA7" data-src="./images/image-24.jpg" alt="" width="800" height="554">
3
   <noscript>
4
      <img src="./images/image-24.jpg" alt="" width="800" height="554">
5
   </noscript>
6
</figure>

¡Está todo listo! Refresca la página y si inspeccionamos la línea de tiempo de la red en DevTools podemos ver que la velocidad de la página está ahora significativamente mejorada solo cargando lo que es visible para lo ususarios.

¡Más Rápido!

La carga de la página está ahora completa en solo 1.95s a una velocidad 3G regular; más del 500% de mejora en velocidad.

Concluyendo

En este tutorial, vimos cómo mejorar la carga de la página cargando imágenes solo cuando el usuario las ve. Este método es popularmente conocido como "carga perezosa" y puede ayudar al rendimiento de tu sitio web enormemente.

Hay muchas librerías JavaScript y complementos jQuery que hacen esto, ¿así que por qué optar por in-view.js? Personalmente, in-view.js ha sido el tipo de script que he estado buscando ya que no trata de hacer demasiado. Solo maneja una cosa y lo hace bien. Este tipo de librería da más control y mejor flexibilidad.

Por ejemplo, no solo podemos usar in-view.js para realizar carga perezosa, sino que también podemos usarla para cosas como hacer scroll infinito, tal vez mostrar un botón flotante de suscripción cuando el usuario llega al final de la página (echa un vistazo al demo para verlo en acción), o crear una línea vertical de tiempo sin tener que jalar otra librería JavaScript. ¡Haznos saber cómo lo usas!

floating subscribe formfloating subscribe formfloating subscribe form
Formulario flotante de suscripción, animado en la vista una vez que el usuario llega al final de la página.

Otros Recursos