
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.
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).
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.
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:
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 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.
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.
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).
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:
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).
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:
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:
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.
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)
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)
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); } } });
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); } }); }); });
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