Melhorando o carregamento das imagens e SVGs

O primeiro e mais importante passo para melhorar a velocidade de carregamento de um site são as imagens. Se você ainda não sabe como medir a velocidade do seu site, o seguinte artigo pode te ajudar:

Medindo a velocidade do seu site

Se já trabalhou otimizando as imagens, talvez tenha interesse em ler sobre outras mudanças que podem melhorar a velocidade:

Melhorando o carregamento do Javascript

Melhorando o carregamento do CSS

Imagens

Formato

Com base em testes feitos em nossas lojas da Nuvem Shop, descobrimos que embora o formato PNG tenha uma boa qualidade, pode gerar um peso desnecessário, que pode ser reduzido em até  10 vezes quando é alterado para o formato JPG. Claro que se você precisa que a imagem tenha o canal alpha (utilize transparência), o JPG não vai poder ser uma opção, mas é importante considerá-lo.

Produto da loja Argentina Unibow,  onde a imagem passou de 2,5mb para 181 kb.

No exemplo de Unibow, o tempo de carregamento das imagens, após a mudança de formato e compressão utilizando ferramentas como TinyPNG, baixou de quase 8.67 segundos para 2.07, são 6 segundos a menos!

Antes (acima) e depois da mudança.

LazyLoad

Por que carregar todas as imagens se o usuário ainda não precisa vê-las? Podem alcançar isso utilizando da técnica de  “LazyLoad” que basicamente carrega todas as imagens que estão acima "above the fold" (dentro do quadro inicial da tela quando o site é carregado) ou as que fiquem próximas de estarem visíveis na tela, e logo serão carregadas progressivamente quando o usuário está fazendo scroll ou abre uma tela ou pop-up que tem as imagens que não foram carregadas.

Desta forma, um enorme tempo é economizado até que o documento finaliza o carregamento das imagens. Por que carregar as imagens que estão no rodapé ou no pop-up no momento que o site é carregado?

Hoje existem dezenas de plugins de JS para utilizar LazyLoad, na Nuvem Shop utilizamos lazysizes que é bastante completo e fácil de implementar. Alguns pontos sobre lazysizes:

  • Não depende de jQuery, importante para carregar o código mínimo bloqueante (Javascript ou CSS)
  • É fácil de utilizar, simplesmente adicionando a class "lazyload" na img, depois a url de uma imagem placeholder (ou pode ser a mesma imagem em baixa qualidade) é adicionada no atributo src e finalmente utilizando o atributo data-src (ou data-srcset se você precisa srcset)  com a url da imagem final de boa qualidade. Uma vez que a imagem é carregada, a class  “lazyload” vai mudar para “lazyloaded”, você pode utilizar as classes para fazer mudanças no CSS uma vez que o carregamento da imagem finalizou.
  • Se estão  utilizando srcset é importante levar em conta as extensões respimg (para os browsers velhos como IE 9 e 11) e bgset caso que precisem utilizar uma imagem com background-image além do srcset.
  • Outro ponto interessante é que o plugin não vai carregar as imagens que tenham ou estejam dentro de um elemento com "display:none;".
  • Se você precisa utilizar ainda mais funcionalidades do plugin, pode dar uma olhada as diferentes extensiões que tem

Placeholders

Você tem LazyLoad, Parabéns! Mas, o que acontece até que a imagem final é exibida? Pergunta CHA-VE

Muitas coisas podem ser feitas até ter a imagem final,  exibir um ícone girando (spinner), exibir um quadrado ou retângulo cinza com o tamanho da imagem, exibir um SVG similar ao que será a imagem (como acontece em muitas apps, por exemplo Facebook), utilizar a mesma imagem em baixa qualidade. Sempre exibir algo, é de muita ajuda para baixar a ansiedade do usuário (em comparação com uma tela em branco ou vazia), deixando ao usuário navegar o conteúdo e melhorando a percepção de velocidade (o site parece carregar mais rápido mas não é assim. Tão importante como a velocidade real já que é o que percebe o usuário.).

Exemplo dum placeholder de Facebook, no artigo “Improved Percieved Performance With Skeleton Screens”

Em nossas lojas o que fizemos foi aplicar a técnica de LWIP (Low Quality Image Placeholders) carregando a imagem com um tamanho pequeno e um efeito de blur até que o lazy load exiba a imagem final. Também nos sliders misturamos isso com um placeholder tipo “wireframe” (utilizamos bxslider e simplesmente ocultamos o placeholder no evento onSliderLoad) :

Para utilizar LQIP, tem que aplicar no atributo  src da tag img , a imagem em baixa qualidade e peso (na Nuvem Shop temos muitas verões da mesma imagem, a mais pequena com 50px de largura, os tamanhos que utilizamos são: tiny de 50px de largura, thumb de 100px, small de 240px, medium de 320px, large de 480px, huge de 640px e original de 1024px) com um CSS que aplica um width de 100% ao seu contêiner e logo quando a imagem é "lazyloaded" é substituida pela imagem de boa qualidade. No meio podem utilizar transições de CSS,  nós aplicamos um efeito de blur animado.


O código fica mais o menos como o seguinte:

<img alt=”{{ product.featured_image.alt }}” data-sizes=”auto” src=”{{ product.featured_image | product_image_url(‘tiny’)}}” data-src=”{{ product.featured_image | product_image_url }}” class=”lazyload item-image img-absolute blur-up” />

Onde você pode notar que utilizamos a class lazysizes para lazysizes, a class  blur-up na transição de “imagem blured” a “imagem final” e os atributos src para a imagem placeholder em baixa qualidade (tiny) e  data-src na imagem final (mais adiante a gente vai utilizar o mesmo exemplo mas com srcset).

O CSS para a transição é o seguinte:

.blur-up {
  -webkit-filter: blur(6px);
  filter: blur(6px);
  transition: filter .5s, -webkit-filter .5s;
}
.blur-up.lazyloaded {
  -webkit-filter: blur(0);
  filter: blur(0);
}

Utilizando a class “lazyloaded” pode ser de ajuda para exibir um ícone e então ocultá-lo quando a imagem foi carregada.

Ainda utilizando o código anterior o resultado não é o melhor, vai ter quebras visuais desde que a imagem pequena é exibida (ou um ícone) até que a imagem final seja carregada. Para evitar isso precisa saber o espaço que vai ocupar a imagem antes que seja carregada e para isso vai precisar conhecer as dimensões de alto e largura desde o backend.
Com essa informação você vai ter que fazer o seguinte:

<div style="padding-bottom: {{ product.featured_image.dimensions['height'] / product.featured_image.dimensions['width'] * 100}}%;">
    <a href="{{ product_url_with_selected_variant }}" title="{{ product.name }}">
        <img alt="{{ product.featured_image.alt }}" data-sizes="auto" src="{{ product.featured_image | product_image_url('tiny')}}" data-src="{{ product.featured_image | product_image_url }}" class="lazyload item-image img-absolute blur-up" />
    </a>
</div>

Depois que a imagem seja carregada vai ficar como a seguinte imagem:

Vai ver duas coisas:
  • Um estilo inline no contêiner da imagem, que basicamente é a conta: alto/largura*100. Essa conta vai dar uma porcentagem que vai utilizar com um padding-bottom , que vai "empurrar" o espaço que a imagem ocupará com a relação de aparência correta (de muita utilidade também em listagens tipo Pinterest). A conta vai dar algo como padding-bottom: 133.68146214099%; .
  • Por outro lado você vai precisar que a imagem fique localizada bem dentro do contêiner sem que seja empurrada pelo  padding adicionado. Para isso precisam utilizar a class img-absolute com o seguinte CSS:
.img-absolute {
  position: absolute;
  left: 0;
  width: 100%;
  height: auto;
  vertical-align: middle;
  text-indent: -9999px;
  z-index: 1;
}

SRCSET

Outra mudança chave é o uso de  srcset, O que é? Basicamente imagens responsivas através de um atributo de HTML.

Adicionando o atributo srcset além do src no tag img, vai poder utilizar muitas urls da mesma imagem em diferentes tamanhos e o navegador vai carregar só a imagem adequada com base a, por exemplo, a largura da tela do dispositivo.


Nas lojas utilizamos a implementação com base na unidade “w”, na qual usamos a largura de cada imagem que temos. Tem muitas maneiras de utilizar srcset, recomendo ler mais sobre isso neste artigo.

Compartilho um exemplo de como é o código de lazyload + srcset:

<div style="padding-bottom: {{ product.featured_image.dimensions['height'] / product.featured_image.dimensions['width'] * 100}}%;">
    <a href="{{ product_url_with_selected_variant }}" title="{{ product.name }}">
        <img alt="{{ product.featured_image.alt }}" data-sizes="auto" src="{{ product.featured_image | product_image_url('tiny')}}" data-src="{{ product.featured_image | product_image_url('small')}} 240w, {{ product.featured_image | product_image_url('medium')}} 320w" class="lazyload item-image img-absolute blur-up" />
    </a>
</div>

SVGs

O SVG também é um formato gráfico como a imagem e mellhora a velocidade de carregamento já que é um vetor que não tem atraso na hora de ser “pintado” pelo navegador como acontece com as imagens onde tem que ser pintadas/carregadas pixel por pixel.

Nosso caso utilizamos SVGs para carregar todos os ícones críticos que precisamos que estejam visíveis nos primeiros segundos do carregamento do site: uma lupa de pesquisa, o carrinho de compras, o hamburger, etc.

Deste jeito vai poder carregar os SVGs muito rápido de maneira inline e deixar o resto dos ícones com uma família tipográfica (nos utilizamos Font Awesome) e sejam carregados de forma assincrônica pelo CSS.

Para utilizar os  SVGs tem que:

  • Criar o SVG num software como pode ser Illustrator e descarregar SVG feito.
  • No Illustrator na opção de "salvar como" tem que escolher o formato SVG Tiny 1.1.

  • Depois tem que escolher a opção código SVG.
  • Finalmente tem que copiar e colar no site SVGOMG que vai ajudar a comprimir o arquivo ainda mais. Logo tem que fazer clique no ícone de "copiar"

Já tem o código SVG pronto para ser colado onde quiser, é importante que fique inline no HTML para evitar um pedido desnecessário do navegador. Na Nuvem Shop geramos um arquivo para cada SVG e logo é importado utilizando Twig e um include, que basicamente faz o mesmo que se fosse colado inline no HTML.

{% include “snipplets/svg/hamburger.tpl” %}

Que segue?

Se já pôde otimizar tudo o que tem a ver com imagens recomendamos continuar lendo as mudanças feitas em outras áreas:

Melhorando o carregamento do Javascript

Melhorando o carregamento do CSS