oscarviedma

Código Funcionalidad OVPC JavaScript - OV DIVI

Oct 17th, 2025
451
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
HTML 15.74 KB | None | 0 0
  1. <!-- Ionicons -->
  2. <script type="module" src="https://unpkg.com/[email protected]/dist/ionicons/ionicons.esm.js"></script>
  3. <script nomodule src="https://unpkg.com/[email protected]/dist/ionicons/ionicons.js"></script>
  4.  
  5. <!-- GSAP -->
  6. <script src="https://cdnjs.cloudflare.com/ajax/libs/gsap/3.12.2/gsap.min.js"></script>
  7.  
  8. <script>
  9.   // Products data
  10.   const products = [
  11.       {
  12.           name: "Obsidian Puffer",
  13.           img: "https://images.unsplash.com/photo-1591047139829-d91aecb6caea?w=400&h=400&fit=crop",
  14.          price: "$240 USD",
  15.          tag: "Outerwear",
  16.          url: "https://ovdivi.com/blog/",
  17.      },
  18.      {
  19.          name: "Slate Joggers",
  20.          img: "https://images.unsplash.com/photo-1594633312681-425c7b97ccd1?w=400&h=400&fit=crop",
  21.          price: "$160 USD",
  22.          tag: "Essentials",
  23.          url: "https://ovdivi.com/blog/",
  24.      },
  25.      {
  26.          name: "Noir Shirt",
  27.          img: "https://images.unsplash.com/photo-1621072156002-e2fccdc0b176?w=400&h=400&fit=crop",
  28.          price: "$190 USD",
  29.          tag: "Classic",
  30.          url: "https://ovdivi.com/blog/",
  31.      },
  32.      {
  33.          name: "Ash Knit",
  34.          img: "https://images.unsplash.com/photo-1576566588028-4147f3842f27?w=400&h=400&fit=crop",
  35.          price: "$220 USD",
  36.          tag: "Core Piece",
  37.          url: "https://ovdivi.com/blog/",
  38.      },
  39.      {
  40.          name: "Form Jacket",
  41.          img: "https://images.unsplash.com/photo-1591085686350-798c0f9faa7f?w=400&h=400&fit=crop",
  42.          price: "$280 USD",
  43.          tag: "Minimal",
  44.          url: "https://ovdivi.com/blog/",
  45.      },
  46.      {
  47.          name: "Carbon Trousers",
  48.          img: "https://images.unsplash.com/photo-1594633313593-bab3825d0caf?w=400&h=400&fit=crop",
  49.          price: "$210 USD",
  50.          tag: "Tailored",
  51.          url: "https://ovdivi.com/blog/",
  52.      },
  53.      {
  54.          name: "Edge Vest",
  55.          img: "https://images.unsplash.com/photo-1434389677669-e08b4cac3105?w=400&h=400&fit=crop",
  56.          price: "$150 USD",
  57.          tag: "Layer",
  58.          url: "https://ovdivi.com/blog/",
  59.      },
  60.      {
  61.          name: "Grain Tee",
  62.          img: "https://images.unsplash.com/photo-1521572163474-6864f9cf17ab?w=400&h=400&fit=crop",
  63.          price: "$130 USD",
  64.          tag: "Everyday",
  65.          url: "https://ovdivi.com/blog/",
  66.      },
  67.      {
  68.          name: "Stone Cap",
  69.          img: "https://images.unsplash.com/photo-1588850561407-ed78c282e89b?w=400&h=400&fit=crop",
  70.          price: "$95 USD",
  71.          tag: "Accessory",
  72.          url: "https://ovdivi.com/blog/",
  73.      },
  74.      {
  75.          name: "Void Coat",
  76.          img: "https://images.unsplash.com/photo-1539533018447-63fcce2678e3?w=400&h=400&fit=crop",
  77.          price: "$310 USD",
  78.          tag: "Statement",
  79.          url: "https://ovdivi.com/blog/",
  80.      },
  81.  ];
  82.  
  83.   // Main slider logic
  84.   const ovPsGallery = document.querySelector(".ov-ps-ovPsGallery");
  85.   const ovPsProductsContainer = document.querySelector(".ov-ps-products");
  86.   const ovPsProductName = document.querySelector(".ov-ps-product-name");
  87.   const ovPsProductNameText = document.querySelector(".ov-ps-product-name p");
  88.   const ovPsProductPreview = document.querySelector(".ov-ps-product-preview");
  89.   const ovPsPreviewOverlay = document.querySelector(".ov-ps-preview-overlay");
  90.   const ovPsPreviewName = document.querySelector(".ov-ps-product-preview-name p");
  91.   const ovPsPreviewImg = document.querySelector(".ov-ps-product-preview-img img");
  92.   const ovPsPreviewTag = document.querySelector(".ov-ps-product-preview-tag p");
  93.   const ovPsPreviewUrl = document.querySelector(".ov-ps-product-url .ov-ps-btn a");
  94.   const ovPsProductBanner = document.querySelector(".ov-ps-product-banner");
  95.   const ovPsBannerImg = document.querySelector(".ov-ps-product-banner img");
  96.   const ovPsViewBtn = document.querySelector(".ov-ps-view-btn");
  97.   const ovPsViewBtnIcon = ovPsViewBtn.querySelector("ion-icon");
  98.   const ovPsPrevBtn = document.querySelector(".ov-ps-nav-btn.ov-ps-prev");
  99.   const ovPsNextBtn = document.querySelector(".ov-ps-nav-btn.ov-ps-next");
  100.   const ovPsDragIndicator = document.querySelector(".ov-ps-drag-indicator");
  101.  
  102.   let currentProductIndex = 0;
  103.   let slideItems = [];
  104.   let isPreviewAnimating = false;
  105.   let isPreviewOpen = false;
  106.   let isDragging = false;
  107.   let dragStartX = 0;
  108.   let dragThreshold = 50;
  109.  
  110.   const BUFFER_SIZE = 5;
  111.   const spacing = 0.375;
  112.   const slideWidth = spacing * 1000;
  113.  
  114.   function addSlideItem(relativeIndex) {
  115.       const productIndex =
  116.           (((currentProductIndex + relativeIndex) % products.length) +
  117.               products.length) %
  118.           products.length;
  119.       const product = products[productIndex];
  120.  
  121.       const li = document.createElement("li");
  122.       li.innerHTML = `<img src="${product.img}" alt="${product.name}" />`;
  123.       li.dataset.relativeIndex = relativeIndex;
  124.  
  125.       li.addEventListener("click", function(e) {
  126.           if (!isDragging && relativeIndex === 0) {
  127.              togglePreview();
  128.           }
  129.       });
  130.  
  131.       gsap.set(li, {
  132.           x: relativeIndex * slideWidth,
  133.           scale: relativeIndex === 0 ? 1.25 : 0.75,
  134.           zIndex: relativeIndex === 0 ? 100 : 1,
  135.           opacity: isPreviewOpen ? 0 : 1,
  136.       });
  137.  
  138.       ovPsProductsContainer.appendChild(li);
  139.       slideItems.push({ element: li, relativeIndex: relativeIndex });
  140.   }
  141.  
  142.   function removeSlideItem(relativeIndex) {
  143.       const itemIndex = slideItems.findIndex(
  144.           (item) => item.relativeIndex === relativeIndex
  145.       );
  146.       if (itemIndex !== -1) {
  147.           const item = slideItems[itemIndex];
  148.           item.element.remove();
  149.           slideItems.splice(itemIndex, 1);
  150.       }
  151.   }
  152.  
  153.   function updateSliderPosition() {
  154.       slideItems.forEach((item) => {
  155.           const isActive = item.relativeIndex === 0;
  156.           gsap.to(item.element, {
  157.               x: item.relativeIndex * slideWidth,
  158.               scale: isActive ? 1.25 : 0.75,
  159.               zIndex: isActive ? 100 : 1,
  160.               duration: 0.75,
  161.               ease: "power3.out",
  162.           });
  163.       });
  164.   }
  165.  
  166.   function updateProductName() {
  167.       const actualIndex =
  168.           ((currentProductIndex % products.length) + products.length) %
  169.           products.length;
  170.       ovPsProductNameText.textContent = products[actualIndex].name;
  171.   }
  172.  
  173.   function updatePreviewContent(animate = true) {
  174.       const actualIndex =
  175.           ((currentProductIndex % products.length) + products.length) %
  176.           products.length;
  177.       const currentProduct = products[actualIndex];
  178.      
  179.       if (!animate) {
  180.           // Solo actualizar contenido sin animar (para inicialización)
  181.           ovPsPreviewName.textContent = currentProduct.name;
  182.           ovPsPreviewImg.src = currentProduct.img;
  183.           ovPsPreviewImg.alt = currentProduct.name;
  184.           ovPsPreviewTag.textContent = currentProduct.tag;
  185.           ovPsPreviewUrl.href = currentProduct.url;
  186.           ovPsPreviewUrl.target = "_blank";
  187.           ovPsPreviewUrl.rel = "noopener noreferrer";
  188.           ovPsPreviewUrl.textContent = `Ver Detalles - ${currentProduct.price}`;
  189.           ovPsBannerImg.src = currentProduct.img;
  190.           ovPsBannerImg.alt = currentProduct.name;
  191.           return;
  192.       }
  193.      
  194.       // Animar el preview hacia abajo primero
  195.       gsap.to(ovPsProductPreview, {
  196.           y: "0%",
  197.           opacity: 0,
  198.           duration: 0.3,
  199.           ease: "power2.in",
  200.           onComplete: () => {
  201.               // Update content
  202.               ovPsPreviewName.textContent = currentProduct.name;
  203.               ovPsPreviewImg.src = currentProduct.img;
  204.               ovPsPreviewImg.alt = currentProduct.name;
  205.               ovPsPreviewTag.textContent = currentProduct.tag;
  206.               ovPsPreviewUrl.href = currentProduct.url;
  207.               ovPsPreviewUrl.target = "_blank";
  208.               ovPsPreviewUrl.rel = "noopener noreferrer";
  209.               ovPsPreviewUrl.textContent = `Ver Detalles - ${currentProduct.price}`;
  210.               ovPsBannerImg.src = currentProduct.img;
  211.               ovPsBannerImg.alt = currentProduct.name;
  212.              
  213.               // Animar el preview desde abajo hacia arriba
  214.               gsap.fromTo(ovPsProductPreview,
  215.                   {
  216.                       y: "20%",
  217.                       opacity: 0
  218.                   },
  219.                   {
  220.                       y: "-50%",
  221.                       opacity: 1,
  222.                       duration: 0.5,
  223.                       ease: "power2.out"
  224.                   }
  225.               );
  226.              
  227.               // Fade banner image
  228.               gsap.to(ovPsBannerImg, {
  229.                   opacity: 1,
  230.                   duration: 0.3
  231.               });
  232.           }
  233.       });
  234.   }
  235.  
  236.   function moveNext() {
  237.       if (isPreviewAnimating) return;
  238.  
  239.       currentProductIndex++;
  240.       removeSlideItem(-BUFFER_SIZE);
  241.       slideItems.forEach((item) => {
  242.           item.relativeIndex--;
  243.           item.element.dataset.relativeIndex = item.relativeIndex;
  244.          
  245.           item.element.onclick = function() {
  246.               if (!isDragging && item.relativeIndex === 0) {
  247.                  togglePreview();
  248.               }
  249.           };
  250.       });
  251.       addSlideItem(BUFFER_SIZE);
  252.       updateSliderPosition();
  253.       updateProductName();
  254.      
  255.       if (isPreviewOpen) {
  256.           updatePreviewContent();
  257.       }
  258.   }
  259.  
  260.   function movePrev() {
  261.       if (isPreviewAnimating) return;
  262.  
  263.       currentProductIndex--;
  264.       removeSlideItem(BUFFER_SIZE);
  265.       slideItems.forEach((item) => {
  266.           item.relativeIndex++;
  267.           item.element.dataset.relativeIndex = item.relativeIndex;
  268.          
  269.           item.element.onclick = function() {
  270.               if (!isDragging && item.relativeIndex === 0) {
  271.                  togglePreview();
  272.               }
  273.           };
  274.       });
  275.       addSlideItem(-BUFFER_SIZE);
  276.       updateSliderPosition();
  277.       updateProductName();
  278.      
  279.       if (isPreviewOpen) {
  280.           updatePreviewContent();
  281.       }
  282.   }
  283.  
  284.   function updateButtonStates() {
  285.       if (isPreviewAnimating) {
  286.           ovPsPrevBtn.classList.add("ov-ps-disabled");
  287.           ovPsNextBtn.classList.add("ov-ps-disabled");
  288.       } else {
  289.           ovPsPrevBtn.classList.remove("ov-ps-disabled");
  290.           ovPsNextBtn.classList.remove("ov-ps-disabled");
  291.       }
  292.   }
  293.  
  294.   function getActiveSlide() {
  295.       return slideItems.find((item) => item.relativeIndex === 0);
  296.   }
  297.  
  298.   function animateSideItems(hide = false) {
  299.       const activeSlide = getActiveSlide();
  300.  
  301.       // Ocultar/mostrar TODOS los slides
  302.       slideItems.forEach((item) => {
  303.           gsap.to(item.element, {
  304.               x: hide
  305.                   ? item.relativeIndex * slideWidth * 1.5
  306.                   : item.relativeIndex * slideWidth,
  307.               opacity: hide ? 0 : 1,
  308.               duration: 0.75,
  309.               ease: "power3.inOut",
  310.           });
  311.       });
  312.  
  313.       // Ajustar el slide activo
  314.       if (activeSlide) {
  315.           gsap.to(activeSlide.element, {
  316.               scale: hide ? 0.75 : 1.25,
  317.               duration: 0.75,
  318.               ease: "power3.inOut",
  319.           });
  320.       }
  321.   }
  322.  
  323.   function togglePreview() {
  324.       if (isPreviewAnimating) return;
  325.  
  326.       isPreviewAnimating = true;
  327.       updateButtonStates();
  328.  
  329.       if (!isPreviewOpen) {
  330.           updatePreviewContent();
  331.           ovPsViewBtn.classList.add("ov-ps-active");
  332.           ovPsViewBtnIcon.name = "close-outline";
  333.           ovPsPreviewOverlay.classList.add("active");
  334.          
  335.           // Cambiar z-index a 0 cuando se abre
  336.           ovPsProductBanner.style.zIndex = "0";
  337.           // Ocultar título con opacidad 0
  338.           ovPsProductName.style.opacity = "0";
  339.       } else {
  340.           ovPsViewBtn.classList.remove("ov-ps-active");
  341.           ovPsViewBtnIcon.name = "eye-outline";
  342.           ovPsPreviewOverlay.classList.remove("active");
  343.          
  344.           // Restaurar z-index a -1 cuando se cierra
  345.           ovPsProductBanner.style.zIndex = "-1";
  346.           // Mostrar título con opacidad 1
  347.           ovPsProductName.style.opacity = "1";
  348.       }
  349.  
  350.       gsap.to(ovPsProductPreview, {
  351.           y: isPreviewOpen ? "100%" : "-50%",
  352.           duration: 0.75,
  353.           ease: "power3.inOut",
  354.       });
  355.      
  356.       gsap.to(ovPsProductBanner, {
  357.           opacity: isPreviewOpen ? 0 : 1,
  358.           duration: 0.4,
  359.           delay: isPreviewOpen ? 0 : 0.25,
  360.           ease: "power3.inOut",
  361.       });
  362.  
  363.       animateSideItems(!isPreviewOpen);
  364.  
  365.       setTimeout(() => {
  366.           isPreviewAnimating = false;
  367.           isPreviewOpen = !isPreviewOpen;
  368.           updateButtonStates();
  369.       }, 600);
  370.   }
  371.  
  372.   function closePreview() {
  373.       if (isPreviewOpen && !isPreviewAnimating) {
  374.          togglePreview();
  375.       }
  376.   }
  377.  
  378.   // Drag functionality
  379.   function handleDragStart(e) {
  380.       if (isPreviewAnimating) return;
  381.      
  382.       isDragging = false;
  383.       dragStartX = e.type.includes('mouse') ? e.clientX : e.touches[0].clientX;
  384.       ovPsGallery.classList.add('dragging');
  385.   }
  386.  
  387.   function handleDragMove(e) {
  388.       if (isPreviewAnimating) return;
  389.       if (!dragStartX) return;
  390.  
  391.       const currentX = e.type.includes('mouse') ? e.clientX : e.touches[0].clientX;
  392.       const diff = currentX - dragStartX;
  393.  
  394.       if (Math.abs(diff) > 5) {
  395.           isDragging = true;
  396.           e.preventDefault();
  397.       }
  398.  
  399.       slideItems.forEach((item) => {
  400.           gsap.to(item.element, {
  401.               x: item.relativeIndex * slideWidth + diff * 0.5,
  402.               duration: 0.1,
  403.           });
  404.       });
  405.   }
  406.  
  407.   function handleDragEnd(e) {
  408.       if (isPreviewAnimating) return;
  409.       if (!dragStartX) return;
  410.  
  411.       const currentX = e.type.includes('mouse') ? e.clientX : e.changedTouches[0].clientX;
  412.       const diff = currentX - dragStartX;
  413.  
  414.       ovPsGallery.classList.remove('dragging');
  415.  
  416.       if (Math.abs(diff) > dragThreshold) {
  417.           if (diff > 0) {
  418.               movePrev();
  419.           } else {
  420.               moveNext();
  421.           }
  422.       } else {
  423.           updateSliderPosition();
  424.       }
  425.  
  426.       dragStartX = 0;
  427.      
  428.       setTimeout(() => {
  429.           isDragging = false;
  430.       }, 100);
  431.   }
  432.  
  433.   // Mouse events
  434.   ovPsGallery.addEventListener('mousedown', handleDragStart);
  435.   ovPsGallery.addEventListener('mousemove', handleDragMove);
  436.   ovPsGallery.addEventListener('mouseup', handleDragEnd);
  437.   ovPsGallery.addEventListener('mouseleave', handleDragEnd);
  438.  
  439.   // Touch events
  440.   ovPsGallery.addEventListener('touchstart', handleDragStart, { passive: false });
  441.   ovPsGallery.addEventListener('touchmove', handleDragMove, { passive: false });
  442.   ovPsGallery.addEventListener('touchend', handleDragEnd);
  443.  
  444.   // ESC key to close preview
  445.   document.addEventListener('keydown', (e) => {
  446.       if (e.key === 'Escape') {
  447.           closePreview();
  448.       }
  449.   });
  450.  
  451.   // Click outside preview to close
  452.   ovPsPreviewOverlay.addEventListener('click', closePreview);
  453.  
  454.   // Hide drag indicator after first interaction
  455.   ovPsGallery.addEventListener('mousedown', () => {
  456.       setTimeout(() => {
  457.           ovPsDragIndicator.style.display = 'none';
  458.       }, 1000);
  459.   }, { once: true });
  460.  
  461.   ovPsGallery.addEventListener('touchstart', () => {
  462.       setTimeout(() => {
  463.           ovPsDragIndicator.style.display = 'none';
  464.       }, 1000);
  465.   }, { once: true });
  466.  
  467.   function initializeSlider() {
  468.       for (let i = -BUFFER_SIZE; i <= BUFFER_SIZE; i++) {
  469.          addSlideItem(i);
  470.      }
  471.      updateSliderPosition();
  472.      updateProductName();
  473.      updatePreviewContent(false); // false = no animar en la inicialización
  474.      updateButtonStates();
  475.  }
  476.  
  477.  ovPsPrevBtn.addEventListener("click", movePrev);
  478.  ovPsNextBtn.addEventListener("click", moveNext);
  479.  ovPsViewBtn.addEventListener("click", togglePreview);
  480.  
  481.  initializeSlider();
  482. </script>
Advertisement
Add Comment
Please, Sign In to add comment