domingo, 17 de agosto de 2014

Acelere suas páginas com vídeos YouTube

Acelere suas páginas com vídeos YouTube

Queremos que os nossos visitantes obtam o seu conteúdo o mais rápido possível, isto é, que seja tão leves quanto possível embora limitando o número de pedidos necessários. Mas também queremos que eles fiquem nossas páginas, e que se entretenham. Este é onde os vídeos entram no jogo. Ilustram o nosso conteúdo textual, dão-lhe mais vida, e são servidos por sites de terceiros. O que perguntar mais? Porém, têm um custo escondido: são lentos e pesados para baixar, mesmo se os visitantes não os olham.
Este post é uma tradução do artigo Faster YouTube Embeds with JavaScript que publiquei no site Sitepoint.

Um só vídeo numa página chamado por um iframe inocente acrescenta até 6 pedidos HTTP e 450kb de conteúdo. A solução aqui proposta pode reduzir este custo numa única consulta e cerca de 50kb por vídeo, mais alguns bits de javascript (em adição à biblioteca jQuery se não gosta do javascript minimalista).

E sabe o quê? Esta solução ainda não é nova. Foi proposta por Amit Agarwal em abril de 2013.

Então, qual é o truque?


Nesta solução, o DOM é analisado pelo javascript no document load, e cada chamada para um vídeo YouTube (via um div específico, não um iframe clássico) é substituído por uma imagem de pré-visualização à qual ligamos o iframe quando clicar neste. Desta maneira, temos uma imagem bonita sempre emitida por um servidor de terceiros por uma fração do preço do player de vídeo (o player de vídeo é carregado só quando o vídeo for visualizado).

O meu pequeno valor acrescentado


Eu reescrevi o código do Amit em javascript clássico e na notação jQuery. Eu mantive os comentários originais no código para o manter tão compreensível quanto possível. No entanto, uma nova funcionalidade parece no parâmetro data do HTML5 permitindo que você adicione qualquer parâmetro à seu URL YouTube para personalizar o seu leitor.

YouTube oferece de facto uma lista de parâmetros para mostrar ou ocultar os controles, o seu logo e as informações relacionadas com o vídeo, e para configurar a qualidade do vídeo ou o quadro de imagem inicial dele.
  • controls: quando pondo o valor 0, a camada de controle não é mais exibida no leitor.
  • modestbranding: quando pondo o valor 1, o logótipo YouTube desaparece da barra de controle.
  • rel: quando pondo o valor 0, nenhum vídeo semelhante é proposto no fim da leitura.
  • showinfo: quando pondo o valor 0, o leitor não exibe mais informações como o título do vídeo ou o nome da pessoa que o baixou antes o vídeo começar.
  • start: quando pondo um número de segundos, o leitor começa a tocar o vídeo a partir deste momento (ou melhor a partir do quadro de imagem chave a mais próxima).
  • vq: diga-lhe a qualidade de vídeo requerida, se for suportada (exemplo: hd720 quando a alta qualidade está disponível)
Quando acrescentamos o iframe de YouTube no evento (clique do mouse ou tecla de retorno), alguns parâmetros recebem um valor predefinido, nomeadamente autoplay (queremos que a leitura começa quando a miniatura é clicada) e autohide (para ocultar a barra de progresso do vídeo e os controlos do leitor quando não há nenhuma interação detetada).

As miniaturas YouTube suportadas


Cada vídeo YouTube vem com uma lista de imagens prefixadas. Você pode encontrá-los através da URL http://img.youtube.com/vi/<youtube-video-id>/<youtube-thumbnail> (onde img.youtube.com ainda pode ser encurtada em i.ytimg.com). Aquelas de interesse são as seguintes:
  • default.jpg (versão predefinida, 120px * 90px)
  • hqdefault.jpg (versão de alta qualidade, 480px × 360px)
  • mqdefault.jpg (versão de qualidade média, 320px × 180px)
  • sddefault.jpg (versão de qualidade tipo, 640px × 480px)
  • maxresdefault.jpg (versão em resolução máxima, 1 280px × 720px)
No código seguinte, usamos a miniatura sddefault.jpg. Dependendo das suas necessidades e das capacidades do ecrã dos seus usuários, pode ser substituída por uma doutras miniaturas definidas acima.

Observação: Esta solução não é responsiva. Convido você a ler os comentários do artigo original para uma adaptação ao ecrã dos seus visitantes.

O código HTML


O código HTML define o identificador do vídeo YouTube, o tamanho do vídeo (largura e altura) e enumera os parâmetros da URL se necessário. Esta implementação também apóia a acessibilidade: o usuário pode clicar sobre a imagem para iniciar o vídeo, ou selecioná-la pressionando a tecla [Tab] (via o atributo tabindex), e depois a tecla de retorno.
    <div class="youtube" id="lR4tJr7sMPM" style="width:500px;height:281px;" tabindex="1"></div>

    <div class="youtube" id="fsrJWUVoXeM" data-params="modestbranding=1&showinfo=0&controls=0&vq=hd720" style="width:640px;height:360px;" tabindex="2"></div>


O código CSS


Em ambos os vídeos utilizados no exemplo, as imagens têm uma relação 16:9, o que retorna uma imagem sddefault.jpg com listras horizontais pretas. Para as esconder quando mostrada a miniatura, a propriedade background-position tem o valor center, e a largura e a altura da imagem são adicionadas diretamente on-line no tag div (style="width:500px;height:281px;"). Desta maneira, é possível mostrar vários tamanhos de vídeo na mesma página.

O ícone de leitura informa os visitantes que o conteúdo não é apenas uma imagem e que podem interagir com ele. Está acrescentada numa camada acima da miniatura com uma transição de opacidade para a destacar. Uso aqui um PNG sob forma de data URI codificado em base 64 (via IconFinder), o que economiza um pedido HTTP e já é compatível com o IE8.
.youtube {
    background-position: center;
    background-repeat: no-repeat;
    position: relative;
    display: inline-block;
    overflow: hidden;
    -webkit-transition: all 200ms ease-out;
    -moz-transition: all 200ms ease-out;
    -o-transition: all 200ms ease-out;
    transition: all 200ms ease-out;
    cursor: pointer;
}

.youtube .play {
    background: url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAEAAAABACAYAAACqaXHeAAAERklEQVR4nOWbTWhcVRTHb1IJVoxGtNCNdal2JYJReC6GWuO83PM/59yUS3FRFARdFlwYP1CfiojQWt36sRCUurRIdVFXIn41lAoVdRGrG1M01YpKrWjiYmaSl8ybZJL3cd+YA//NLObd3++eO8x79z5jSq5Gw+8kov0AP8vMR5l1BtBZQM4B8ks75wCdZdYZZj5qLZ4hov2Nht9Z9vhKKSIaB/gI4M4w62KeAO6Mte4lYOq20FxrlqqOibhHmeWbvNC9ZfDX1mLae391aN6limO/gwgvAPJbWeAZuSDingdwXTBw7/0IsyaA/Fkh+KqOkD+YNfHej1QKD+y7iVlOhgLvFqFfNJvNGyuBJ+KDAF8MDd0tgS8y64OlgSdJMsysL4cG7SOHkyQZLhTee7+d2R2rAVy/S+Jd7/32ouBHAP4gNNRGQyTHc/84NhqNywZp5rvjjnnvt21aABFeCQ+RLwAf2hQ8s7sv9OCLk6AHNgQvIrvbfzKCD76g/O6cu7lf/iER/aQGgy448pExZmhdegAPhR9sObFWH1gT3lp7DaA/5bkIgJhZPgsNmz02novj+KqeApj1ubwXWe4kdyeznAgNvTpE/HQmvKqOMeuFogTUVQSRno+iaLRLAJF7uIgL9O4ubgL8aWgB7S44mNX+35YpICUiAvS9sBLkq1WzT+NFffl6AuoiApi6NT37h6sWkBIRZGkQ8YtLgyji6e1mBYTqCEBPG2Naz+0BWQgtoGoRgCzEsd9hAN1X5BfnFZASUfrSAFQNsyZ1FJASUVpHiLinDJG8U2cBZYogkrcNs5waBAGdstbeU9zdqpw0gPwwSAI6VUxHyFlDpOcHUUBBIuYNs14aZAE5RVwyzPr3/0EAEY0TyfGNjBWQvwZ+CTSbehfAH29mrID8bET0+0EUkAd8WYDOmqJ3ecsG30yr9wqRfm6Y+a1BEFDEjHfHvWmY9ck6CygHvBVr8Xhtb4ZE5HZA3y8DvBNA1TjnrmXWf+sioMwZX5V/VHXMGGMMoKdDCxCRvRWBdzKzdHEO+EisilbPyopHYqp6S9UCAsz4iojI7hUDAtyXVQgIDd6KnOoaWNkbI6FaPSuZGyMArsi7MZoloB4zviI/Nhr3X95jltwTRQmoIfgisy5ai+me67OI7fE4nrqjrqfK1t0eby0FPRB6oGVlchL3rgnfrq19RKbVBdhV9IOSwJmfmJi4vi/4ThERitwyCxVAFqydshuCX5awhQ9KtmuIWd8IDZED/nXT77rvVVv6sHRKwjYi91poqP7Dr+Y6JJ1VSZIMA3wkPNy6bX+o8Bcm0sXMdwM8Fxo0A3xORPaWBp6uPXsmbxCRD0NDL0dOANhVCXy6iAjMcjbcrMt3RITKwdMVRdFo+y5yvkL4eWZ+zHt/ZVD4dEVRNGotpst+dZZZH8k86lqn2pIvT/eqrNfn2xuyqYPZ8mv7s8pfn/8Pybm4TIjanscAAAAASUVORK5CYII=") no-repeat center center;
    background-size: 64px 64px;
    position: absolute;
    height: 100%;
    width: 100%;
    opacity: .8;
    filter: alpha(opacity=80);
    -webkit-transition: all 0.2s ease-out;
    -moz-transition: all 0.2s ease-out;
    -o-transition: all 0.2s ease-out;
    transition: all 0.2s ease-out;
}

.youtube .play:hover {
    opacity: 1;
    filter: alpha(opacity=100);
}

Implementação em javascript clássico


Sem qualquer dependência e com a implementação a mais rápida, a versão em javascript clássico usa aqui o menor teste de carregamento do DOM que consegui encontrar. As especificidades dos navegadores devem ser tomadas em conta, como a falta de apoio da função getElementsByClassName pelo IE8 (se você quiser apoiá-lo).
"use strict";
function r(f){/in/.test(document.readyState)?setTimeout('r('+f+')',9):f()}
r(function(){
    if(!document.getElementsByClassName) {
        // IE8 support
        var getElementsByClassName = function(node, classname) {
            var a = [];
            var re = new RegExp('(^| )'+classname+'( |$)');
            var els = node.getElementsByTagName("*");
            for(var i=0,j=els.length; i<j; i++)
                if(re.test(els[i].className))a.push(els[i]);
            return a;
        }
        var videos = getElementsByClassName(document.body,"youtube");
    }
    else {
        var videos = document.getElementsByClassName("youtube");
    }

    var nb_videos = videos.length;
    for (var i=0; i<nb_videos; i++) {
        // Based on the YouTube ID, we can easily find the thumbnail image
        videos[i].style.backgroundImage = 'url(http://i.ytimg.com/vi/' + videos[i].id + '/sddefault.jpg)';

        // Overlay the Play icon to make it look like a video player
        var play = document.createElement("div");
        play.setAttribute("class","play");
        videos[i].appendChild(play);

        videos[i].onkeypress = function(event) {
            // return key
            if (event.keyCode == 13) {
                document.getElementById(this.id).click();
            }
        }

        videos[i].onclick = function() {
            // Create an iFrame with autoplay set to true
            var iframe = document.createElement("iframe");
            var iframe_url = "https://www.youtube.com/embed/" + this.id + "?autoplay=1&autohide=1";
            if (this.getAttribute("data-params")) iframe_url+='&'+this.getAttribute("data-params");
            iframe.setAttribute("src",iframe_url);
            iframe.setAttribute("frameborder",'0');

            // The height and width of the iFrame should be the same as parent
            iframe.style.width  = this.style.width;
            iframe.style.height = this.style.height;

            // Replace the YouTube thumbnail with YouTube Player
            this.parentNode.replaceChild(iframe, this);
        }
    }
});

Ver o Pen Faster YouTube embeds (vanilla js) por Alexis Ulrich (@mancko) em CodePen.



Observação sobre o modo estrito do ECMAScript5


O modo estrito do ECMAScript5 ajuda a escrever um código javascript mais portátil. Para o ativar, o código javascript deve começar por:
"use strict";
Neste modo estrito, as definições de funções podem ser declaradas ao mais alto nível, ou ao mais alto nível do corpo da função. Então o código seguinte tem um erro de sintaxe.
if(!document.getElementsByClassName) {
    function getElementsByClassName(node, classname) {
Isto é corrigido usando a notação seguinte (somente num bloco):
if(!document.getElementsByClassName) {
    var getElementsByClassName = function(node, classname) {


Implementação com o jQuery


Embora esteja aos meus olhos mais expressiva e beneficiando de um apoio mais amplo dos navegadores, a implementação com o jQuery vem com o custo de adicionar a biblioteca jQuery (cerca de 82ko para a versão mais recente).
"use strict";
$(function() {
    $(".youtube").each(function() {
        // Based on the YouTube ID, we can easily find the thumbnail image
        $(this).css('background-image', 'url(http://i.ytimg.com/vi/' + this.id + '/sddefault.jpg)');

        // Overlay the Play icon to make it look like a video player
        $(this).append($('<div/>', {'class': 'play'}));

        // accessibility handling: click on mouse left button (keycode 1) or [Return] key (keycode 13)
        $(document).delegate('#'+this.id, 'click keydown', function(event) {
            if (event.which == 1 || event.which == 13) {
                event.preventDefault();

                // Create an iFrame with autoplay set to true
                var iframe_url = "https://www.youtube.com/embed/" + this.id + "?autoplay=1&autohide=1";
                if ($(this).data('params')) iframe_url+='&'+$(this).data('params');

                // The height and width of the iFrame should be the same as parent
                var iframe = $('<iframe/>', {'frameborder': '0', 'src': iframe_url, 'width': $(this).width(), 'height': $(this).height() })

                // Replace the YouTube thumbnail with YouTube HTML5 Player
                $(this).replaceWith(iframe);
            }
        });
    });
});

Ver o Pen Faster YouTube embeds (jQuery style) por Alexis Ulrich (@mancko) em CodePen.



Resultados


Falamos agora sobre o que você pode ganhar numa situação real.

Esta solução foi implementada na página «As cantigas de Santa Maria e o galaico-português», um artigo contindo três vídeos YouTube. Aqui estão os resultados:
  • Antes de implementar esta solução, tivemos 20 pedidos HTTP, 636,2ko de conteúdo transferido, o que levava 2,22s (3,59s onload)
  • Uma vez implementada, descemos para 17 pedidos HTTP, 370,7ko de conteúdo, e um tempo de carregamento de 1,05s (733ms onload)
  • Isto significa 15% menos pedidos, uma página 41% mais leve e 52% mais rápida (80% mais rápida para o onload)

Os resultados já são bons, mesmo com um único vídeo YouTube, como na página «Étymologie du yoga», o nosso segundo exemplo. Aqui estão os resultados:
  • Antes de implementar esta solução, tivemos 20 pedidos HTTP, 684,4ko de conteúdo transferido, o que levava 2,13s (2,14s onload)
  • Uma vez implementada, descemos para 17 pedidos HTTP, 322,4ko de conteúdo, e um tempo de carregamento de 1,24s (975ms onload)
  • Isto significa 15% menos pedidos, uma página 53% mais leve e 42% mais rápida (54% mais rápida para o onload)

Conclusão


Eu acho que todos nós concordamos para dizer que reduzir o peso de uma página de 40% até 50% vale a pena, pois não?

Se você tem idéias para melhorar este código, pode fazer um fork no CodePen (javascript clássico ou versão jQuery). Também os seus comentários são bem-vindos, quer neste blog, quer no artigo publicado em Sitepoint.

Crédito da foto: Espelho de Cinzas

Accélérez vos pages contenant des vidéos YouTube (em francês)
Acelere sus páginas con vídeos de YouTube (em espanhol)

Sem comentários:

Enviar um comentário