Slajder produktów v2 — 4Hand

Slajderem jest sam moduł Galeria.
Na module Galeria wpisujesz klasę sp2-slajder — i to wszystko.
Moduł Kod niesie wyłącznie CSS i JS, nie tworzy żadnej własnej struktury i niczego nie ukrywa.

Czym różni się od wersji 1

W wersji 1 skrypt czytał galerię, budował obok drugą strukturę i chował oryginał. Galeria znikała też w kreatorze, więc nie dało się w nią kliknąć. Tutaj galeria zostaje na swoim miejscu — widoczna i edytowalna.

Jak dołożyć produkt

  1. Klikasz w GalerięAktualizuj galerię → zaznaczasz zdjęcia w bibliotece mediów.
  2. Napis na pionowym pasku bierze się z pola Tytuł zdjęcia w bibliotece mediów — wpisz tam nazwę modelu, np. classic-012. Robi to sam CSS przez content: attr(title), bez udziału JS.
  3. Kolejność ustawiasz przeciąganiem w oknie wyboru zdjęć.
  4. W ustawieniach Galerii podnieś liczbę zdjęć i wyłącz stronicowanie.

Ustawienia

Zmienne na .sp2-slajder, na samej górze kodu.

  • --sp2-na-ekranie — ile kafli widać naraz (10 / 5 / 3 zależnie od szerokości ekranu)
  • --sp2-odstep — przerwa między kaflami
  • --sp2-proporcja — kształt kafla
  • --sp2-przerwa — co ile milisekund następny krok
  • --sp2-przejscie — jak długo trwa krok
  • --sp2-powiekszenie — o ile rośnie kafel po najechaniu

Wariant A — powiększenie z Divi

Klasa na module Galeria: sp2-slajder
Kliknięcie obsługuje lightbox wbudowany w Divi. Mniej kodu po naszej stronie, wygląd spójny z resztą galerii na serwisie. Klony potrzebne do zapętlenia trafiają do lightboxa, więc lista w powiększeniu ma podwójną długość.

Wariant B — powiększenie własne

Klasy na module Galeria: sp2-slajder sp2-wlasny
Kliknięcie przechwytuje nasz kod. Lista w powiększeniu ma dokładnie tyle pozycji, ile jest produktów. Działają strzałki i Esc. Cena: kilkadziesiąt linii kodu więcej do utrzymania.

Kod do skopiowania

Jeden moduł Kod obsługuje wszystkie slajdery na stronie, razem ze znacznikami.

<style>
/* =====================================================
   SLAJDER PRODUKTOW v2 - sama Galeria Divi
   Klasa na module Galeria:  sp2-slajder
   Dodatkowo (wariant B):    sp2-wlasny
   Ten kod nie tworzy zadnej wlasnej struktury - przerabia
   na slajder to, co Divi juz wypisalo.
   ===================================================== */

.sp2-slajder{
  --sp2-na-ekranie: 10;      /* ile kafli widac naraz */
  --sp2-odstep: 30px;        /* przerwa miedzy kaflami */
  --sp2-proporcja: 128/215;  /* ksztalt kafla */
  --sp2-przerwa: 3000;       /* co ile ms nastepny krok */
  --sp2-przejscie: 1500;     /* jak dlugo trwa krok */
  --sp2-powiekszenie: 1.05;  /* o ile rosnie kafel po najechaniu */
  --sp2-zolty: #F0C609;

  /* modul galerii jest oknem podgladu; padding-bottom robi miejsce
     na kafel rosnacy w dol - przycinanie dziala dopiero na krawedzi
     wypelnienia, wiec powiekszony kafel i cien pozostaja widoczne */
  overflow: hidden;
  padding-bottom: 26px;
}

/* siatka galerii staje sie torem slajdera */
.sp2-slajder .et_pb_gallery_items{
  display: flex !important;
  flex-wrap: nowrap !important;
  gap: var(--sp2-odstep);
  will-change: transform;
}

/* kazda pozycja galerii staje sie kaflem */
.sp2-slajder .et_pb_gallery_item{
  flex: 0 0 calc((100% - (var(--sp2-na-ekranie) - 1) * var(--sp2-odstep)) / var(--sp2-na-ekranie)) !important;
  width: auto !important;
  margin: 0 !important;
  padding: 0 !important;
  float: none !important;
  clear: none !important;
  position: relative;
  overflow: hidden;
  background: #000;
  transform-origin: top center;   /* gora stoi w miejscu, kafel rosnie w dol */
  transition: transform .5s cubic-bezier(.2,0,.2,1), box-shadow .5s ease;
}
.sp2-slajder .et_pb_gallery_item:hover{
  transform: scale(var(--sp2-powiekszenie));
  z-index: 5;
  box-shadow: 0 0 20px 0 rgba(0,0,0,.35);
}

.sp2-slajder .et_pb_gallery_image{
  display: block;
  position: relative;
  overflow: hidden;
  aspect-ratio: var(--sp2-proporcja);
  margin: 0 !important;
}
.sp2-slajder .et_pb_gallery_image a{ display:block; width:100%; height:100%; position:relative; }
.sp2-slajder .et_pb_gallery_image img{
  display: block; width: 100%; height: 100%; object-fit: cover;
}

/* wlasna nakladka zamiast lupki Divi */
.sp2-slajder .et_overlay{ display:none !important; }

/* zolty kwadrat + dwie kreski jadace od krawedzi = plus */
.sp2-slajder .et_pb_gallery_image a::before{
  content:""; position:absolute; inset:0; z-index:3; opacity:0;
  background-image:
    linear-gradient(#fff,#fff),
    linear-gradient(#fff,#fff),
    linear-gradient(var(--sp2-zolty),var(--sp2-zolty));
  background-repeat:no-repeat;
  background-size:3px 26px, 26px 3px, 56px 56px;
  background-position:50% 0%, 0% 50%, 50% 50%;
  transition:opacity .25s ease, background-position .5s cubic-bezier(.2,0,.2,1);
  pointer-events:none;
}
.sp2-slajder .et_pb_gallery_item:hover .et_pb_gallery_image a::before{
  opacity:1;
  background-position:50% 50%, 50% 50%, 50% 50%;
}

/* pionowa etykieta - tytul zdjecia siedzi juz w atrybucie title odnosnika,
   wiec bierzemy go wprost, bez pomocy JS */
.sp2-slajder .et_pb_gallery_image a::after{
  content: attr(title);
  position:absolute; top:0; right:0; bottom:0; width:24px; z-index:4;
  writing-mode:vertical-rl; background:rgba(0,0,0,.5); color:#fff;
  font-size:13px; font-weight:300; letter-spacing:3px; text-transform:uppercase;
  display:flex; align-items:center; justify-content:center;
  padding:5px 0; overflow:hidden; pointer-events:none;
}

/* elementy galerii, ktore w slajderze sa zbedne */
.sp2-slajder .et_pb_gallery_title,
.sp2-slajder .et_pb_gallery_caption,
.sp2-slajder .et_pb_gallery_pagination{ display:none !important; }

/* strzalki dokladane przez JS */
.sp2-slajder .sp2-arrows{ display:flex; gap:48px; justify-content:center; margin-top:10px; }
.sp2-slajder .sp2-arrows button{
  background:none; border:0; cursor:pointer; color:var(--sp2-zolty);
  font:600 14px/1 Poppins,system-ui,Arial,sans-serif; letter-spacing:2px;
  display:inline-flex; align-items:center; gap:10px; padding:8px;
}
.sp2-slajder .sp2-arrows button:hover{ color:#323232; }
.sp2-slajder .sp2-prev::before{ content:"\2190"; font-size:19px; }
.sp2-slajder .sp2-next::after{ content:"\2192"; font-size:19px; }

@media (max-width:990px){ .sp2-slajder{ --sp2-na-ekranie:5; } }
@media (max-width:590px){ .sp2-slajder{ --sp2-na-ekranie:3; --sp2-odstep:15px; } }

/* wlasne powiekszenie - tylko przy klasie sp2-wlasny */
.sp2-lightbox{
  position:fixed; inset:0; z-index:99999; background:rgba(0,0,0,.92);
  display:flex; align-items:center; justify-content:center;
  opacity:0; visibility:hidden; transition:opacity .25s ease, visibility .25s;
}
.sp2-lightbox.jest-otwarty{ opacity:1; visibility:visible; }
.sp2-lightbox img{ max-width:88vw; max-height:84vh; display:block; }
.sp2-lb-podpis{
  position:absolute; left:0; right:0; bottom:22px; text-align:center; color:#fff;
  letter-spacing:3px; text-transform:uppercase; font-size:13px;
}
.sp2-lb-zamknij,.sp2-lb-prev,.sp2-lb-next{
  position:absolute; background:none; border:0; color:#fff; cursor:pointer;
  font-size:34px; line-height:1; padding:14px;
}
.sp2-lb-zamknij:hover,.sp2-lb-prev:hover,.sp2-lb-next:hover{ color:#F0C609; }
.sp2-lb-zamknij{ top:10px; right:14px; }
.sp2-lb-prev{ left:10px; top:50%; transform:translateY(-50%); }
.sp2-lb-next{ right:10px; top:50%; transform:translateY(-50%); }
</style>

<script>
(function(){
  function start(){
    document.querySelectorAll('.sp2-slajder').forEach(function(root){
      if(root.dataset.sp2) return; root.dataset.sp2 = '1';

      var tor = root.querySelector('.et_pb_gallery_items');
      if(!tor || !tor.children.length) return;

      var styl = getComputedStyle(root);
      var PRZERWA   = parseFloat(styl.getPropertyValue('--sp2-przerwa'))   || 3000;
      var PRZEJSCIE = parseFloat(styl.getPropertyValue('--sp2-przejscie')) || 1500;

   /* spis zdjec zbierany PRZED klonowaniem */
      var lista = [].map.call(tor.children, function(poz){
        var a = poz.querySelector('a');
        return { src: a ? a.getAttribute('href') : '', tytul: a ? (a.getAttribute('title') || '') : '' };
      });
      [].forEach.call(tor.children, function(poz, i){ poz.dataset.i = i; });

      var oryg = tor.children.length;
      tor.innerHTML = tor.innerHTML + tor.innerHTML;   /* klon -> plynne zapetlenie */

   /* strzalki */
      var strzalki = document.createElement('div');
      strzalki.className = 'sp2-arrows';
      strzalki.innerHTML = '<button class="sp2-prev" type="button">PREV</button>'
                         + '<button class="sp2-next" type="button">NEXT</button>';
      root.appendChild(strzalki);

      var offset = 0, tween = null, ostatni = null;
      var doNastepnego = PRZERWA, najechane = false, lbOtwarty = false;
      var bezRuchu = window.matchMedia('(prefers-reduced-motion: reduce)').matches;

      function krok(){
        var a = tor.children[0].getBoundingClientRect();
        var b = tor.children[1].getBoundingClientRect();
        return b.left - a.left;
      }
      function jednostka(){ return krok() * oryg; }
      function ease(p){ return 1 - Math.pow(1 - p, 3); }
      function przesun(kierunek, czas){
        var od = tween ? tween.to : offset;
        tween = { from: offset, to: od + kierunek * krok(), start: performance.now(), dur: czas };
      }

      strzalki.querySelector('.sp2-prev').addEventListener('click', function(){ przesun(-1, 450); doNastepnego = PRZERWA; });
      strzalki.querySelector('.sp2-next').addEventListener('click', function(){ przesun( 1, 450); doNastepnego = PRZERWA; });

      root.addEventListener('mouseenter', function(){ najechane = true; });
      root.addEventListener('mouseleave', function(){ najechane = false; });

      function klatka(t){
        if(ostatni === null) ostatni = t;
        var dt = t - ostatni; ostatni = t;

        if(tween){
          var p = (t - tween.start) / tween.dur;
          if(p >= 1){ offset = tween.to; tween = null; doNastepnego = PRZERWA; }
          else { offset = tween.from + (tween.to - tween.from) * ease(p); }
        } else if(!najechane && !lbOtwarty && !bezRuchu){
          doNastepnego -= dt;
          if(doNastepnego <= 0) przesun(1, PRZEJSCIE);
        }

        var u = jednostka();
        if(u > 0) offset = ((offset % u) + u) % u;
        tor.style.transform = 'translateX(' + (-offset) + 'px)';
        requestAnimationFrame(klatka);
      }
      requestAnimationFrame(klatka);

      /* ---------- powiekszenie ----------
         Bez klasy sp2-wlasny nic tu nie robimy - klikniecie obsluguje
         lightbox wbudowany w Galerie Divi. */
      if(!root.classList.contains('sp2-wlasny')) return;

      var lb = document.createElement('div');
      lb.className = 'sp2-lightbox';
      lb.innerHTML = '<button class="sp2-lb-zamknij" type="button" aria-label="Zamknij">&times;</button>'
                   + '<button class="sp2-lb-prev" type="button" aria-label="Poprzednie">&#8249;</button>'
                   + '<img alt=""><span class="sp2-lb-podpis"></span>'
                   + '<button class="sp2-lb-next" type="button" aria-label="Nastepne">&#8250;</button>';
      document.body.appendChild(lb);
      var lbImg = lb.querySelector('img'), lbPodpis = lb.querySelector('.sp2-lb-podpis');
      var biezacy = 0;

      function pokaz(i){
        biezacy = ((i % lista.length) + lista.length) % lista.length;
        lbImg.src = lista[biezacy].src;
        lbImg.alt = lista[biezacy].tytul;
        lbPodpis.textContent = lista[biezacy].tytul;
      }
      function otworz(i){ pokaz(i); lb.classList.add('jest-otwarty'); lbOtwarty = true; }
      function zamknij(){ lb.classList.remove('jest-otwarty'); lbOtwarty = false; }

      tor.addEventListener('click', function(e){
        var poz = e.target.closest('.et_pb_gallery_item');
        if(!poz) return;
        e.preventDefault();
        e.stopPropagation();
        otworz(parseInt(poz.dataset.i, 10));
      }, true);

      lb.querySelector('.sp2-lb-zamknij').addEventListener('click', zamknij);
      lb.querySelector('.sp2-lb-prev').addEventListener('click', function(e){ e.stopPropagation(); pokaz(biezacy - 1); });
      lb.querySelector('.sp2-lb-next').addEventListener('click', function(e){ e.stopPropagation(); pokaz(biezacy + 1); });
      lb.addEventListener('click', function(e){ if(e.target === lb || e.target === lbImg) zamknij(); });
      document.addEventListener('keydown', function(e){
        if(!lbOtwarty) return;
        if(e.key === 'Escape')     zamknij();
        if(e.key === 'ArrowLeft')  pokaz(biezacy - 1);
        if(e.key === 'ArrowRight') pokaz(biezacy + 1);
      });
    });
  }
  if(document.readyState === 'loading') document.addEventListener('DOMContentLoaded', start);
  else start();
})();
</script>