Video para un producto

En el este artículo vamos a agregar un video para el detalle de producto, mostrándose al final de la galería de imágenes:

Esta funcionalidad, así como el video en la página de inicio, permite mostrar videos de Youtube y Vimeo. Si ya lo tenes implementado en tu tienda para el inicio, vas a notar algunas pequeñas diferencias que te recomendamos que hagas.

HTML

1. Dentro de la carpeta snipplets crear un nuevo archivo llamado video-item.tpl,  el cual usaremos tanto para el video como para la miniatura del mismo. Este componente usa lazy load, si no lo tenés en tu sitio te recomendamos este artículo.

{% if product_modal %}

    {# Product video modal wrapper #}

    <div id="product-video-modal" class="js-product-video-modal product-video" style="display: none;">
{% endif %}
        <div class="js-video {% if product_video %}js-video-product{% endif %} embed-responsive embed-responsive-16by9 visible-when-content-ready">

            {% if product_modal_trigger %}

                {# Open modal in mobile with product video inside #}

                <a href="#product-video-modal" data-fancybox="product-gallery" class="js-play-button video-player d-block d-md-none">
                    <div class="video-player-icon">{% include "snipplets/svg/play-circle.tpl" with {svg_custom_class: "icon-inline svg-icon-invert"} %}</div>
                </a>
            {% endif %}
            <a href="#" class="js-play-button video-player {% if product_modal_trigger %}d-none d-md-block{% endif %}">
                <div class="video-player-icon">
                    {% include "snipplets/svg/play-circle.tpl" with {svg_custom_class: "icon-inline svg-icon-invert"} %}
                </div>
            </a>
            <div class="js-video-image">
                <img src="{{ 'images/empty-placeholder.png' | static_url }}" data-src="" class="lazyload video-image fade-in" alt="{{ 'Video de' | translate }} {% if template != 'product' %}{{ product.name }}{% else %}{{ store.name }}{% endif %}" style="display: none;">
                <div class="placeholder-fade">
                </div>
            </div>
        </div>
            
        {# Empty iframe component: will be filled with JS on play button click #}


        <div class="js-video-iframe embed-responsive embed-responsive-16by9" style="display: none;" data-video-color="{{ settings.primary_color | trim('#') }}">
        </div>
{% if product_modal %}
    </div>
{% endif %}

2. Luego creamos otro archivo con el nombre product-video.tpl dentro de la carpeta snipplets/product, que usaremos para incluirlo en el detalle del producto. De esta manera el video se mostrará a lo último de las imágenes.

{% if product.video_url %}
    {% if product.images_count > 1 %}
        {% set video_index = product.images_count %}
    {% else %}
        {% set video_index = 1 %}
    {% endif %}
    <div class="js-product-slide js-product-video-slide swiper-slide slider-slide" data-image-position="{{ video_index }}">
        <div class="product-video-container">
            <div class="product-video">
                {# Visible video inside slider #}
                {% include 'snipplets/video-item.tpl' with {product_modal_trigger: true, product_video: true} %}

                {# Hidden video inside modal #}
                {% include 'snipplets/video-item.tpl' with {product_modal: true, product_video: true} %}
            </div>
        </div>
    </div>
{% endif %}

3. Una vez creados esos dos snipplets, vamos a ir al archivo product-image.tpl y justo donde termina el “for” que itera sobre las imágenes del producto, agregamos el llamado a product-video.tpl. Debería quedar algo como lo siguiente:

{% set has_multiple_slides = product.images_count > 1 or product.video_url %}

{% if product.images_count > 0 %}
    <div class="js-swiper-product nube-slider-product swiper-container" style="visibility:hidden; height:0;">
        {% include 'snipplets/labels.tpl' with {'product_detail': true} %}
        <div class="swiper-wrapper">
            {% for image in product.images %}
             <div class="swiper-slide js-product-slide slider-slide" data-image="{{image.id}}" data-image-position="{{loop.index0}}">
                 <a href="{{ image | product_image_url('huge') }}" data-fancybox="product-gallery" class="d-block p-relative" style="padding-bottom: {{ image.dimensions['height'] / image.dimensions['width'] * 100}}%;">
                     <img src="{{ 'images/empty-placeholder.png' | static_url }}" data-srcset='{{  image | product_image_url('large') }} 480w, {{  image | product_image_url('huge') }} 640w' data-sizes="auto" class="js-product-slide-img product-slider-image img-absolute img-absolute-centered lazyautosizes lazyload" {% if image.alt %}alt="{{image.alt}}"{% endif %}/>
                     <img src="{{ image | product_image_url('tiny') }}" class="js-product-slide-img product-slider-image img-absolute img-absolute-centered blur-up" {% if image.alt %}alt="{{image.alt}}"{% endif %} />
                </a>
             </div>
            {% endfor %}
            {% include 'snipplets/product/product-video.tpl' %}
        </div>
        <div class="js-swiper-product-pagination swiper-pagination swiper-pagination-white"></div>
        {% if has_multiple_slides %}
            <div class="js-swiper-product-prev swiper-button-prev d-none d-md-block">{% include "snipplets/svg/chevron-left.tpl" with {svg_custom_class: "icon-inline icon-w-8 icon-2x svg-icon-text"} %}</div>
            <div class="js-swiper-product-next swiper-button-next d-none d-md-block">{% include "snipplets/svg/chevron-right.tpl" with {svg_custom_class: "icon-inline icon-w-8 icon-2x svg-icon-text"} %}</div>
        {% endif %}
    </div>
    {% snipplet 'placeholders/product-detail-image-placeholder.tpl' %}
{% endif %}

Mirando el código de cerca vamos a notar un condicional has_multiple_slides  el cual es definido al comienzo del archivo y lo usamos para mostrar las flechas del slider cuando hay muchas imágenes o por lo menos una imagen y un video.

Por otro lado es importante mencionar que el código utiliza clases de un slider hecho con Swiper.

4. Vamos a necesitar agregar una clase en el archivo labels.tpl (o donde tengas tus carteles de oferta, envío gratis, etc) para conectar con el Javascript que ocultará estos mensajes cuando se esté viendo el video.

En el div que engloba todos los labels vamos a agregar el condicional {% if product.video_url and product %}js-labels-group{% endif %}, quedando de la siguiente forma:

{% if show_labels %}
  <div class="{% if product.video_url and product %}js-labels-group{% endif %} labels">
    {% if not product.has_stock %}
    ….
  </div>
{% endif %}

5. Agregá el archivo del SVG para el icono “play” del video. Este se va a llamar play-circle.tpl y lo vamos a crear en la carpeta snipplets/svg con el siguiente código

<svg class="{{ svg_custom_class }}" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512"><path d="M371.7 238l-176-107c-15.8-8.8-35.7 2.5-35.7 21v208c0 18.4 19.8 29.8 35.7 21l176-101c16.4-9.1 16.4-32.8 0-42zM504 256C504 119 393 8 256 8S8 119 8 256s111 248 248 248 248-111 248-248zm-448 0c0-110.5 89.5-200 200-200s200 89.5 200 200-89.5 200-200 200S56 366.5 56 256z"/></svg>

6. Esta parte es opcional, si ya tenías implementado el video en la página de inicio, ahora podes usar el archivo video-item.tpl para ahorrar código. En el archivo home-video.tpl simplemente reemplazá todo el código por este:

{% if settings.video_embed %}
    <section class="section-video-home" data-store="video-home">
        <div class="container{% if settings.video_full %}-fluid p-0{% endif %}">
            <div class="row no-gutters">
                <div class="col">
                    {% include 'snipplets/video-item.tpl' %}
                </div>
            </div>
        </div>
    </section>
{% endif %}

CSS

Requisito:

Tener agregados en tu diseño las clases helpers. Podés seguir este este pequeño tutorial para hacerlo (simplemente es copiar y pegar algunas clases, no toma más de 1 minuto).

1. Agregar los estilos dentro del archivo static/style-colors.scss.tpl 

 Agregamos el siguiente SASS de colores en style-colors.scss.tpl (o la hoja de tu diseño que tenga los colores y tipografías de la tienda). Recordá que las variables de colores y tipografías pueden variar respecto a tu diseño:

.product-video-container {
  background-color: rgba($main-foreground, .07);
}

.embed-responsive {
  background: $main-foreground;
}

2. Agregar los estilos dentro del archivo static/style-critical.tpl 

Si en tu diseño usas una hoja de estilos para el CSS crítico, vamos a necesitar agregar el código debajo dentro de la misma, pero si no es el caso podés agregarlo en tu hoja de CSS general.

{# /* // Video */ #}

.embed-responsive {
  position: relative;
  display: block;
  height: 0;
  padding: 0;
  overflow: hidden;
}
.embed-responsive.embed-responsive-16by9 {
  padding-bottom: 56.25%;
}
.embed-responsive .embed-responsive-item,
.embed-responsive embed,
.embed-responsive  iframe,
.embed-responsive  object,
.embed-responsive  video {
  position: absolute;
  top: 0;
  bottom: 0;
  left: 0;
  width: 100%;
  height: 100%;
  border: 0;
}
.video-player {
  position: absolute;
  top: 0;
  left: 0;
  z-index: 1;
  width: 100%;
  height: 100%;
  cursor: pointer;
}
.video-player-icon {
  position: absolute;
  top: 50%;
  left: 50%;
  width: 60px;
  height: 60px;
  margin: -30px 0 0 -30px;
  padding: 0;
  font-size: 60px;
  line-height: 30px;
  text-align: center;
  pointer-events: none;
}
.video-image {
  position: absolute;
  top: 50%;
  left: 50%;
  width: 100%;
  height: auto;
  transform: translate(-50%, -50%);
  -webkit-transform: translate(-50%, -50%);
  -ms-transform: translate(-50%, -50%);
}

.product-video-container {
  display: block;
  width: 100%;
  height: 100%;
}
.product-video {
  height: 100%;
  display: flex;
  align-items: center;
  justify-content: center;
  -webkit-box-align: center;
  -ms-flex-align: center;
  align-items: center;
}
.product-video .embed-responsive {
  width: 100%;
  height: 100%;
  padding-bottom: 0;
}
.product-video .video-image{
  width: auto;
  height: 100%;
}

{# /* // Min width 768px */ #}

@media (min-width: 768px) { 
    {# /* //// Product detail */ #}

    .product-video .video-image,
    .product-video .embed-responsive {
        width: 100%;
        height: auto;
    }

    .product-video .embed-responsive {
        padding-bottom: 56.25%;
    }
}

JS

⚠️ A partir del día 30 de enero de 2023, la librería jQuery será removida del código de nuestras tiendas, por lo tanto la función "$" no podrá ser utilizada.

1. El JavaScript necesitamos agregarlo en el archivo store.js.tpl (o donde tengas tus funciones de JS). El código que necesitamos es el siguiente:

{% if template == 'product' and product.video_url %}
    {% set video_url = product.video_url %}
{% endif %}

{% if video_url %}

    {# /* // Youtube or Vimeo video for home or each product */ #}

    LS.loadVideo('{{ video_url }}');

{% endif %}

En el caso que ya tengas un video en la página de inicio, simplemente borrá el código que tenías y en lugar del código que vemos arriba, agregá lo siguiente:


{% if template == 'home' %}
    {% set video_url = settings.video_embed %}
{% elseif template == 'product' and product.video_url %}
    {% set video_url = product.video_url %}
{% endif %}

{% if video_url %}

    {# /* // Youtube or Vimeo video for home or each product */ #}

    LS.loadVideo('{{ video_url }}');
    
{% endif %}

2.  Vamos a agregar condicionales dentro del slider para el detalle del producto, ubicado en el archivo store.js.tpl . Podemos encontrarlo en con la siguiente línea:

'.js-swiper-product',

Lo primero que vamos a hacer es definir una condición para entender cuando hay muchos slides:

{% set has_multiple_slides = product.images_count > 1 or video_url %}

Luego dentro del evento “init” donde sabemos que el slider ya inició, agregamos el código para que el slide del video tome el alto del slider y se vea centrado verticalmente. Debería quedar algo así

init: function () {
    jQueryNuvem(".js-product-slider-placeholder").hide();
    jQueryNuvem(".js-swiper-product").css("visibility", "visible").css("height", "auto");
    {% if video_url %}
        productSwiperHeight = jQueryNuvem(".js-swiper-product").height();
        jQueryNuvem(".js-product-video-slide").height(productSwiperHeight);
    {% endif %}
},

Justo debajo vamos a agregar un evento para cuando se cambia de slide y así poder ocultar o mostrar los carteles de oferta, descuentos y envío gratis cuando el slide visible es un video.

{% if video_url %}
    slideChangeTransitionEnd: function () {
        if(jQueryNuvem(".js-product-video-slide").hasClass("swiper-slide-active")){
            jQueryNuvem(".js-labels-group").fadeOut(100);
        }else{
            jQueryNuvem(".js-labels-group").fadeIn(100);
        }
        jQueryNuvem('.js-video').show();
        jQueryNuvem('.js-video-iframe').hide().find("iframe").remove();
    },
{% endif %}

Toda la función del slider debería quedar así:

{% set has_multiple_slides = product.images_count > 1 or video_url %}

var productSwiper = null;
createSwiper(
    '.js-swiper-product',
    {
        lazy: true,
        loop: false,
        pagination: {
            el: '.js-swiper-product-pagination',
            type: 'fraction',
            clickable: true,
        },
        navigation: {
            nextEl: '.js-swiper-product-next',
            prevEl: '.js-swiper-product-prev',
        },
        on: {
            init: function () {
                jQueryNuvem(".js-product-slider-placeholder").hide();
                jQueryNuvem(".js-swiper-product").css("visibility", "visible").css("height", "auto");
                {% if video_url %}
                    productSwiperHeight = jQueryNuvem(".js-swiper-product").height();
                    jQueryNuvem(".js-product-video-slide").height(productSwiperHeight);
                {% endif %}
            },
            {% if video_url %}
                slideChangeTransitionEnd: function () {
                    if(jQueryNuvem(".js-product-video-slide").hasClass("swiper-slide-active")){
                        jQueryNuvem(".js-labels-group").fadeOut(100);
                    }else{
                        jQueryNuvem(".js-labels-group").fadeIn(100);
                    }
                    jQueryNuvem('.js-video').show();
                    jQueryNuvem('.js-video-iframe').hide().find("iframe").remove();
                },
            {% endif %}
        },
    },
    function(swiperInstance) {
        productSwiper = swiperInstance;
    }
);

Traducciones

Para terminar con el código agregamos los textos para las traducciones en el archivo config/translations.txt

es "Video de"
pt "Vídeo de"
en "Video of"
es_mx "Video de"

Activación

Podés activar la funcionalidad desde el formulario de cualquier producto, simplemente vas a la parte que dice “Video” y pegas tu link de Youtube o Vimeo.