domingo, 17 de agosto de 2014

Acelere sus páginas con vídeos de YouTube

Acelere sus páginas conteniendo vídeos de YouTube

Queremos que nuestros visitantes obtengan su contenido lo más pronto posible, es decir que debería ser lo más ligero posible limitando al mismo tiempo el número de peticiones necesarias. Pero también queremos que se queden en nuestras páginas, y que se entretienen. Aquí es donde los videos entran en juego. Ilustran nuestro contenido textual, le dan más vida, y son servidos por sitios de terceros. ¿Qué más preguntar? Sin embargo, tienen un coste oculto: son lentos y pesados para descargar, incluso si sus visitantes no los vean.
Este post es la traducción del artículo Faster YouTube Embeds with JavaScript que he publicado en el sitio Sitepoint.

Un sólo vídeo en una página llamado por un inocente iframe agrega hasta 6 peticiones HTTP y 450kb de contenido. La solución aquí propuesta puede reducir este coste a una sola petición y cerca de 50kb por vídeo, más unos pocos octetos de javascript (además de la biblioteca jQuery si que no le gusta el javascript minimalista).

¿Y sabe qué? Esta solución no es nueva. Ha sido propuesta por Amit Agarwal en abril de 2013.

Entonces, ¿cuál es el truco?


En esta solución, el DOM está analizado por javascript en el document load, y cada llamada a un vídeo YouTube (vía un div específico, no un iframe clásico) está sustituido por una imagen de previsualización a la cual unimos el iframe cuando se hace un clic. De este modo, se obtiene una imagen bonita todavía servida por un servidor tercero a una fracción del precio del reproductor de vídeo completo (el reproductor de vídeo sólo se carga cuando se ve el vídeo).

Mi pequeño valor añadido


He reescrito el código de Amit en javascript clásico y en notación jQuery. He mantenido los comentarios originales en el código para mantenerlo lo más comprensible posible. No obstante, una nueva funcionalidad aparece en el parámetro data del HTML5 que le permite agregar cualquier parámetro a su URL YouTube para personalizar el reproductor.

YouTube de hecho ofrece una lista de parámetros para mostrar u ocultar los controles, su logotipo y la información asociada con el vídeo, y también para configurar la calidad del vídeo o su cuadro inicial.
  • controls: paselo a 0, y la capa de control ya no se muestra en el reproductor.
  • modestbranding: paselo a 1, y el logotipo YouTube desaparece de la barra de control.
  • rel: paselo a 0, y ningún vídeo similar será ofrecido al final de la reproducción.
  • showinfo: paselo a 0, y el reproductor no mostrará más información como el título del vídeo o la persona que lo descargó antes de que el vídeo comienza a reproducirse.
  • start: dale un número de segundos, y el reproductor comenzará a reproducir el vídeo desde este momento (o más bien desde el fotograma clave más cercano).
  • vq: digale la calidad de vídeo requerida, si está apoyada (exemplo: hd720 cuando la alta calidad está disponible)
Cuando añadimos el iframe de YouTube en el evento (clic del ratón o tecla de retorno), algunos parámetros reciben un valor predefinido, a saber autoplay (queremos que comience la reproducción tan pronto como se hace clic en la miniatura) y autohide (para ocultar la barra de progreso del vídeo y los controles del reproductor cuando no se detecta ninguna interacción).

Las miniaturas YouTube apoyadas


Cada vídeo YouTube viene con una lista de imágenes pre-generadas. Puede encontrarlas a través del URL http://img.youtube.com/vi/<youtube-video-id>/<youtube-thumbnail> (onde img.youtube.com incluso puede acortarse en i.ytimg.com). Las de mayor interés son las siguientes:
  • default.jpg (versión por defecto, 120px * 90px)
  • hqdefault.jpg (versión de alta calidad, 480px × 360px)
  • mqdefault.jpg (versión de calidad media, 320px × 180px)
  • sddefault.jpg (versión de calidad estándar, 640px × 480px)
  • maxresdefault.jpg (versión en máxima resolución, 1 280px × 720px)
En el siguiente código, utilizamos la miniatura sddefault.jpg. Dependiendo de sus necesidades y de las capacidades de la pantalla de sus usuarios, puede ser sustituida por una de las otras miniaturas definidas por encima.

Ojo: Esta solución no es responsive. Los invito a leer los comentarios del artículo original para una adaptación a la pantalla de sus visitantes.

El código HTML


El código HTML define el identificador del vídeo YouTube, el tamaño del vídeo (anchura y altura) y enumera los parámetros del URL si es necesario. Esta implementación también apoya la accesibilidad: el usuario puede hacer clic en la imagen para iniciar el vídeo, o bien seleccionarla pulsando la tecla [Tab] (a través del atributo tabindex) y luego la 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>


El código CSS


En ambos vídeos utilizados en el ejemplo, las imágenes tienen un ratio 16:9, lo que devuelve una imagen sddefault.jpg con rayas horizontales negras. Para ocultarlas cuando se muestra la miniatura, la propiedad background-position toma el valor center, y la anchura y altura de la imagen se añaden directamente en línea en la etiqueta div (style="width:500px;height:281px;"). De este modo, se puede mostrar diferentes tamaños de vídeo en la misma página.

El icono de reproducción indica a los visitantes que el contenido no es sólo una imagen y que pueden interactuar con él. Se añade en una capa sobre la miniatura con una transición de opacidad para destacarlo. Utilizo aquí un PNG en forma de data URI codificado en base 64 (vía IconFinder), lo que ahorra una petición HTTP y ya es compatible con 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);
}

Implementación en javascript clásico


Sin ningún tipo de dependencia y con la implementación la más rápida, la versión en javascript clásico usa aquí el más pequeño test de carga del DOM que pude encontrar. Las características específicas de los navegadores deben tenerse en cuenta, como la falta de apoyo de la función getElementsByClassName por IE8 (si quiere soportarlo).
"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 el Pen Faster YouTube embeds (vanilla js) por Alexis Ulrich (@mancko) en CodePen.



Nota sobre el modo estricto del ECMAScript5


El modo estricto del ECMAScript5 ayuda a escribir un código javascript más portátil. Para activarlo, el código javascript debe comenzar con:
"use strict";
En este modo estricto, las definiciones de funciones pueden ser declaradas en el nivel más alto, o en el nivel más alto del cuerpo de la función. Así que el siguiente código tiene un error de sintaxis.
if(!document.getElementsByClassName) {
    function getElementsByClassName(node, classname) {
Esto se corrige usando la siguiente notación (sólo en un bloque):
if(!document.getElementsByClassName) {
    var getElementsByClassName = function(node, classname) {


Implementación con jQuery


Aunque más expresiva para mí y beneficiándose de un apoyo más amplio de los navegadores, la implementación con jQuery viene a costa de la adición de la biblioteca jQuery (cerca de 82kb para su última versión).
"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 el Pen Faster YouTube embeds (jQuery style) por Alexis Ulrich (@mancko) en CodePen.



Resultados


Hablamos ahora de lo que se puede ganar en una situación real.

Esta solución se ha implementado en la página «Las Cantigas de Santa María y el galaicoportugués», un artículo que contiene tres videos YouTube. Aquí están los resultados:
  • Antes de implementar esta solución, teníamos 20 peticiones HTTP, 636,2kb de contenido descargado, lo que llevaba 2,22s (3,59s onload)
  • Una vez implementada, bajamos a 17 peticiones HTTP, 370,7kb de contenido, y un tiempo de carga de 1,05s (733ms onload)
  • Esto significa 15% menos peticiones, una página 41% más ligera y 52% más rápida (80% más rápida por el onload)

Los resultados ya son buenos incluso con un solo vídeo YouTube, como en la página «Étymologie du yoga», nuestro segundo ejemplo. Aquí están los resultados:
  • Antes de implementar esta solución, teníamos 20 peticiones HTTP, 684,4kb de contenido descargado, lo que llevaba 2,13s (2,14s onload)
  • Una vez implementada, bajamos a 17 peticiones HTTP, 322,4kb de contenido, y un tiempo de carga de 1,24s (975ms onload)
  • Esto significa 15% menos peticiones, una página 53% más ligera y 42% más rápida (54% más rápida por el onload)

Conclusión


Creo que todos estamos de acuerdo para decir que reducir el peso de una página de 40% hasta 50% vale la pena, ¿verdad?

Si tiene ideas para mejorar este código, puede hacer un fork en CodePen (javascript clásico, o versión jQuery). Sus comentarios son también bienvenidos, ya sea en este blog, o en el artículo publicado en Sitepoint.

Crédito de la foto: Espejo de Cenizas

Accélérez vos pages contenant des vidéos YouTube (en francés)
Acelere suas páginas com vídeos YouTube (en portugués)

No hay comentarios:

Publicar un comentario