Temporizador con colección - Bsale-IO/template-docs GitHub Wiki
Este componente está integrado en todos los templates base y permite añadir un temporizador visual interactivo en la página. El temporizador puede configurarse para hacer una cuenta regresiva hasta una fecha específica, y puede incluir un mensaje personalizado, además ira conectado a una colección de productos. Una vez llegue a cero, el temporizador desaparecerá automáticamente.
Codigo componente Temporizador con colección de productos
<!-- Configuración del temporizador -->
{% assign collection_timer_year = "2026" %} <!-- Año de finalización de la oferta -->
{% assign collection_timer_month = "3" %} <!-- Mes de finalización (marzo) -->
{% assign collection_timer_day = "21" %} <!-- Día de finalización -->
{% assign collection_timer_hour = "15" %} <!-- Hora de finalización (24h) -->
{% assign collection_timer_minute = "46" %} <!-- Minuto de finalización -->
{% assign collection_timer_second = "59" %} <!-- Segundo de finalización -->
<!-- Configuración del texto del temporizador -->
{% assign collection_timer_message = "¡Oferta exclusiva por tiempo limitado!" %} <!-- Mensaje mostrado junto al temporizador -->
{% if collection.size > 0 %}
{% assign discount_format = "corner-badge" %}
{% assign show_hover_image = true %}
{% assign cant_producto_mostrar = "8" %}
{% assign addToCart = true %}
<div class="collection-timer-container mt-4">
<div class="collection-timer-title" title="{{ collection.title }}">
<a href="{{ collection.link }}">{{ collection.title }}</a>
</div>
<div class="collection-timer-content">
<div class="collection-timer-box">
<span class="collection-days">00</span>
<div class="collection-timer-label">Días</div>
</div>
<div class="collection-timer-box">
<span class="collection-hours">00</span>
<div class="collection-timer-label">Horas</div>
</div>
<div class="collection-timer-box">
<span class="collection-minutes">00</span>
<div class="collection-timer-label">Minutos</div>
</div>
<div class="collection-timer-box">
<span class="collection-seconds">00</span>
<div class="collection-timer-label">Segundos</div>
</div>
</div>
<div class="collection-timer-message">
<svg class="timer-icon" width="20" height="20" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M12 2C6.48 2 2 6.48 2 12s4.48 10 10 10 10-4.48 10-10S17.52 2 12 2Zm0 15a1.5 1.5 0 1 1 0-3 1.5 1.5 0 0 1 0 3Zm1-5h-2V7h2v5Z" fill="currentColor"/>
</svg>
{{ collection_timer_message }}
</div>
<!-- Flecha animada que apunta hacia abajo -->
<div class="scroll-arrow-container">
<svg class="scroll-arrow scroll-down-arrow" viewBox="0 0 24 24" xmlns="http://www.w3.org/2000/svg" style="cursor: pointer;">
<path d="M7.41 8.59L12 13.17l4.59-4.58L18 10l-6 6-6-6 1.41-1.41z"/>
</svg>
</div>
</div>
<div class="collection-container">
<!-- Sección de productos -->
<section class="bs-collection-temporizer grid products-section">
{% for product in collection limit: cant_producto_mostrar %}
<div class="bs-collection__product border" data-bs="product" data-info="{{ product.id }}">
<a href="{{ product.link }}" title="{{ product.title }}">
<div class="bs-img-square hover">
{% if product.discountRate > 0 %}
<div class="bs-discount {{ discount_format }}">
<span>-{{ product.discountRate }}%</span>
</div>
{% endif %}
{% assign outStock = false %}
{% if product.classification != 1 %}
{% if product.totalStock < 1 and product.stockControl == 1 %}
{% if product.allowNegativeStock == 0 %}
{% assign outStock = true %}
<div class="bs-collection__product-stock">Sin Stock</div>
{% endif %}
{% endif %}
{% endif %}
<!-- Imágenes con hover -->
<picture
{% if show_hover_image and product.images %}
{% for img in product.images %}
{% if img.href != product.default_image %}
style="--image-hover:url('{{ img.href | image_url: 'M' }}')"
{% break %}
{% endif %}
{% endfor %}
{% endif %}
>
<source {{ product.default_image | source_tag_attributes: 'L' }}>
<source {{ product.default_image | source_tag_attributes: 'M' }}>
<source {{ product.default_image | source_tag_attributes: 'S' }}>
<img
{% if product.default_image.size > 0 %}
{{ product.default_image | img_tag_attributes: 'S' }}
onerror="this.onerror=null;this.src='{{ product.default_image }}';"
{% else %}
{{ site.logo | img_tag_attributes: 'S' }}
{% endif %}
alt="{{ product.title }}"
loading="lazy"
>
</picture>
</div>
</a>
<a class="bs-collection__product-info" href="{{ product.link }}" title="{{ product.title }}">
<h3 class="bs-collection__product-title">
{{ product.title | strip_html | truncate: 60, '...' }}
</h3>
{% if product.notice %}
<div class="bs-collection__product-notice">
{{ product.notice | strip_html | truncate: 60, '...' }}
</div>
{% endif %}
{% if product.brand.name %}
<span class="bs-collection__product-brand">{{ product.brand.name }}</span>
{% endif %}
</a>
<div class="bs-collection__product-price">
{% if product.discount_rate > 0 %}
<del class="bs-collection__old-price">
{{ product.fpWithoutDiscount | money_filter }}
</del>
{% endif %}
{% if product.discount_cant > 1 %}
<small class="bs-collection__discount-condition">
Por compras desde {{ product.discount_cant | round }} unidades
</small>
{% endif %}
<strong class="bs-collection__product-final-price {% if product.discount_rate > 0 %}has-discount{% endif %}">
{{ product.final_price | money_filter }}
</strong>
</div>
{% if addToCart %}
<div class="bs-product-cart mt-auto">
{% if outStock %}
<button class="btn btn-primary mt-1 invisible t{{ product.unlimitedStock }}" disabled>
Agotado
</button>
{% else %}
<button class="btn btn-primary mt-1 t{{ product.unlimitedStock }}" data-bs="cart.add.collection">
Agregar al carro
</button>
{% endif %}
</div>
{% endif %}
</div>
{% endfor %}
</section>
<div class="text-center mt-3">
<a class="btn btn-secondary btn-collection-show" href="{{ collection.link }}">
Ver todos los productos
</a>
</div>
</div>
<script>
// Función principal que inicia y actualiza la cuenta regresiva de la colección
function startCollectionCountdown(targetDate, serverTime) {
// Obtiene los elementos del DOM donde se mostrará la cuenta regresiva y la colección
const collectionCountdownElement = document.querySelector(".collection-timer-container");
const collectionContainerElement = document.querySelector(".collection-container");
const collectionDaysElement = document.querySelector(".collection-days");
const collectionHoursElement = document.querySelector(".collection-hours");
const collectionMinutesElement = document.querySelector(".collection-minutes");
const collectionSecondsElement = document.querySelector(".collection-seconds");
// Variable que lleva el tiempo actual del servidor, se incrementa cada segundo
let currentServerTime = serverTime;
// Función interna que actualiza los valores de la cuenta regresiva
function updateCollectionCountdown() {
// Incrementa el tiempo del servidor en 1000ms (1 segundo)
currentServerTime += 1000;
// Calcula el tiempo restante entre la fecha objetivo y el tiempo actual del servidor
const timeLeft = targetDate - currentServerTime;
// Si el tiempo restante es menor o igual a 0, oculta el temporizador y la colección
if (timeLeft <= 0) {
collectionCountdownElement.classList.add("collection-timer-expired");
collectionContainerElement.classList.add("collection-expired");
return;
}
// Calcula los días, horas, minutos y segundos restantes
const days = Math.floor(timeLeft / (1000 * 60 * 60 * 24));
const hours = Math.floor((timeLeft % (1000 * 60 * 60 * 24)) / (1000 * 60 * 60));
const minutes = Math.floor((timeLeft % (1000 * 60 * 60)) / (1000 * 60));
const seconds = Math.floor((timeLeft % (1000 * 60)) / 1000);
// Actualiza los valores en la UI con dos dígitos
collectionDaysElement.textContent = days.toString().padStart(2, "0");
collectionHoursElement.textContent = hours.toString().padStart(2, "0");
collectionMinutesElement.textContent = minutes.toString().padStart(2, "0");
collectionSecondsElement.textContent = seconds.toString().padStart(2, "0");
}
// Ejecuta la función de actualización inmediatamente para mostrar los valores iniciales
updateCollectionCountdown();
// Configura un intervalo para actualizar la cuenta regresiva cada segundo
setInterval(updateCollectionCountdown, 1000);
}
// Configuración de la fecha objetivo del temporizador en UTC
const collectionOfferEndDate = Date.UTC(
parseInt("{{ collection_timer_year }}"),
parseInt("{{ collection_timer_month }}") - 1, // Restamos 1 porque los meses en JS van de 0 a 11
parseInt("{{ collection_timer_day }}"),
parseInt("{{ collection_timer_hour }}"),
parseInt("{{ collection_timer_minute }}"),
parseInt("{{ collection_timer_second }}")
);
// Inicia la cuenta regresiva pasando la fecha objetivo y el tiempo inicial del servidor
startCollectionCountdown(collectionOfferEndDate, {{serverTimeMs}});
// Agrega un evento de clic a la flecha animada para desplazar la vista a la sección de productos
document.querySelector('.scroll-down-arrow').addEventListener('click', function() {
// Obtiene el elemento de la sección de productos
const productsSection = document.querySelector('.products-section');
// Si la sección existe, desplaza suavemente la vista hacia ella
if (productsSection) {
productsSection.scrollIntoView({
behavior: 'smooth',
block: 'center'
});
}
});
</script>
{% endif %}
Important
en el componente donde esta la sección de el producto, es importante recalcar que los componentes tienen variaciones por el diseño de la web
Codigo CSS del componente Temporizador con colección
.collection-timer-title a {
color: var(--collection-timer-title-text-color);
text-decoration: none;
font-weight: bold;
transition: color 0.3s ease;
}
.collection-timer-title a:hover {
color: rgba(255, 255, 255, 0.8);
}
.collection-timer-container {
position: relative;
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
background: var(--collection-timer-bg);
color: var(--collection-timer-title-text-color);
padding: 20px;
border-radius: 12px;
font-size: 20px;
font-weight: bold;
text-align: center;
margin-bottom: 20px;
box-shadow: 0 8px 15px rgba(0, 0, 0, 0.3), 0 0 0 1px rgba(255, 255, 255, 0.1) inset;
transition: box-shadow 0.3s ease;
}
.collection-timer-container::before {
content: '';
position: absolute;
top: -6px;
left: -6px;
right: -6px;
bottom: -6px;
background: var(--collection-timer-bg);
border-radius: 14px;
opacity: 0;
z-index: -1;
animation: pulse-border-timer 2s infinite ease-in-out;
filter: blur(3px);
}
@keyframes pulse-border-timer {
0% {
transform: scale(1);
opacity: 0;
}
20% {
transform: scale(1.01);
opacity: 0.4;
}
70% {
transform: scale(1.02);
opacity: 0;
}
100% {
transform: scale(1);
opacity: 0;
}
}
.collection-timer-container:hover {
box-shadow: 0 12px 20px rgba(0, 0, 0, 0.4), 0 0 0 1px rgba(255, 255, 255, 0.2) inset;
}
.collection-timer-title {
font-size: 24px;
margin-bottom: 10px;
text-transform: uppercase;
letter-spacing: 1px;
}
.collection-timer-content {
display: flex;
gap: 15px;
font-size: 22px;
}
.collection-timer-box {
display: flex;
flex-direction: column;
align-items: center;
background: var(--exclusive-timer-box-bg);
color: var(--exclusive-timer-box-text-color);
padding: 15px 20px;
border-radius: 12px;
min-width: 80px;
box-shadow: 0px 4px 8px rgba(0, 0, 0, 0.2);
transition: all 0.3s ease;
}
.collection-timer-box:hover {
transform: scale(1.05);
}
.collection-timer-box span {
font-size: 32px;
font-weight: bold;
text-shadow: 2px 2px 4px rgba(0, 0, 0, 0.3);
transition: opacity 0.3s ease;
}
.collection-timer-label {
font-size: 16px;
text-transform: uppercase;
margin-top: 5px;
}
.collection-timer-message {
font-size: 18px;
margin: 15px 0;
background: var(--collection-timer-highlight-bg);
color: var(--collection-timer-highlight-text);
padding: 10px 20px;
border-radius: 50px;
font-weight: bold;
letter-spacing: 0.5px;
animation: scaleAnimation 1.5s infinite;
display: flex;
align-items: center;
justify-content: center;
}
.timer-icon {
animation: flameFlicker 1s infinite;
margin: 0 5px;
}
@keyframes scaleAnimation {
0%, 100% { transform: scale(1); }
50% { transform: scale(1.03); }
}
@keyframes flameFlicker {
0%, 100% { opacity: 1; }
50% { opacity: 0.8; }
}
/* Estilos para la flecha animada */
.scroll-arrow-container {
display: flex;
justify-content: center;
margin-top: 15px;
margin-bottom: 5px;
}
.scroll-arrow {
width: 40px;
height: 40px;
fill: var(--scroll-arrow-color);
animation: bounce 2s infinite;
}
@keyframes bounce {
0%, 20%, 50%, 80%, 100% {
transform: translateY(0);
}
40% {
transform: translateY(10px);
}
60% {
transform: translateY(5px);
}
}
/* Clase específica para ocultar elementos del temporizador */
.collection-timer-expired {
display: none !important;
}
/* Clase específica para ocultar la colección cuando expira */
.collection-expired {
display: none !important;
}
@media (max-width: 480px) {
.collection-timer-content {
display: flex;
flex-wrap: nowrap;
justify-content: center;
padding: 11px;
gap: 5px;
overflow: hidden;
}
.collection-timer-box {
min-width: 70px;
padding: 12px 16px;
}
.collection-timer-box span {
font-size: 24px;
}
.collection-timer-label {
font-size: 11px;
}
.collection-timer-message {
font-size: 16px;
padding: 6px 12px;
}
.scroll-arrow {
width: 30px;
height: 30px;
}
}
Codigo CSS de las variables en el root
/********************************************************************
Temporizador con colección de productos
*********************************************************************/
--collection-timer-bg: linear-gradient(135deg, #1e3c72, #2a5298);
--collection-timer-title-text-color: #fff;
--collection-timer-box-bg: rgba(255, 255, 255, 0.15);
--collection-timer-box-text-color: #fff;
--collection-timer-highlight-bg: yellow;
--collection-timer-highlight-text: black;
--scroll-arrow-color: #ffffff;
El componente "Temporizador con coleccion" para poder activarlo, se deberá llamar en el componente 🟢 INICIO
{{ 'Celulares' | get_collection: 'Temporizador con coleccion' }}
EL principio del componente tiene sus variables de configuración la cual son las siguientes:
<!-- Configuración del temporizador -->
{% assign collection_timer_year = "2026" %} <!-- Año de finalización de la oferta -->
{% assign collection_timer_month = "3" %} <!-- Mes de finalización (marzo) -->
{% assign collection_timer_day = "21" %} <!-- Día de finalización -->
{% assign collection_timer_hour = "15" %} <!-- Hora de finalización (24h) -->
{% assign collection_timer_minute = "46" %} <!-- Minuto de finalización -->
{% assign collection_timer_second = "59" %} <!-- Segundo de finalización -->
<!-- Configuración del texto del temporizador -->
{% assign collection_timer_message = "¡Oferta exclusiva por tiempo limitado!" %} <!-- Mensaje mostrado junto al temporizador -->
Este archivo contiene la configuración del temporizador para la colección de productos exclusivos.
Important
El temporizador funcionará exactamente hasta la fecha y hora configuradas en las variables de tiempo y desaparecerá cuando llegue a cero, incluyendo los productos de la colección
-
collection_timer_year
: Año de finalización de la oferta (Ejemplo:"2026"
). -
collection_timer_month
: Mes de finalización (Ejemplo:"3"
para marzo). -
collection_timer_day
: Día de finalización de la oferta (Ejemplo:"21"
). -
collection_timer_hour
: Hora de finalización en formato 24h (Ejemplo:"15"
para las 15:00 hrs). -
collection_timer_minute
: Minuto exacto en que finaliza la oferta (Ejemplo:"46"
). -
collection_timer_second
: Segundo exacto en que finaliza la oferta (Ejemplo:"59"
).
-
collection_timer_message
: Mensaje mostrado junto al temporizador (Ejemplo:"¡Oferta exclusiva por tiempo limitado!"
).
Important
La variable {{serverTimeMs}}
obtiene la hora directamente desde el servidor, incluyendo la zona horaria. Por lo tanto, no puede ser manipulada de forma local en el equipo del usuario.