Slajder produktów v3 — wariant C

Klasa na module Galeria: sp3-slajder
Mechanizm budowy jak w wariancie A — slajderem jest sam moduł Galeria, powiększenie obsługuje Divi.
Inna jest geometria kafli.

Czym różni się od wariantu A

  • Wspólna wysokość, własna szerokość. Wszystkie kafle mają tę samą wysokość, więc szereg się nie rozsypuje, ale każdy jest tak szeroki, jak wymagają proporcje jego zdjęcia.
  • Nic nie jest kadrowane. Zdjęcie widać w całości — poziome zajmuje po prostu więcej miejsca niż pionowe.
  • Bez napisu z boku i bez przyciemnienia. Zostaje sam żółty plus po najechaniu.

W zestawie niżej stoi 8 zdjęć drzwi (pionowych) i 2 zdjęcia montażu (poziome) — właśnie po to, żeby było widać, że różne proporcje nie psują szeregu.

Jak liczony jest krok

Skoro kafle mają różne szerokości, przesunięcie „o jedno zdjęcie” przestaje być stałą liczbą pikseli. Slajder celuje więc w lewą krawędź kafla o danym numerze (offsetLeft), a nie w z góry ustaloną odległość. Równe szerokości są tu tylko szczególnym przypadkiem, więc ten sam kod obsłuży oba układy.

Wielkość przesuwu

Nastawa --sp3-przesuw przyjmuje dwa zapisy:

  • --sp3-przesuw: 1 — o jeden kafel. Można też 2 albo 3.
  • --sp3-przesuw: 50% — o połowę szerokości okna galerii.
  • --sp3-przesuw: 100% — o całe okno, czyli strona po stronie.

Przy zapisie procentowym slajder nie odmierza pikseli na sztywno — przesuwa się o tyle kafli, ile trzeba, żeby pokryć zadaną część okna. Dzięki temu zawsze kończy na krawędzi kafla i żadne zdjęcie nie zostaje przecięte w połowie. Liczba kafli w kroku sama się zmienia, gdy w szeregu trafi się zdjęcie poziome.

Pozostałe ustawienia

  • --sp3-wysokosc — wspólna wysokość kafli (260 / 210 / 170 px zależnie od szerokości ekranu)
  • --sp3-odstep — przerwa między kaflami
  • --sp3-przerwa — co ile milisekund następny krok
  • --sp3-przejscie — jak długo trwa krok
  • --sp3-powiekszenie — o ile rośnie kafel po najechaniu

Do porównania: wariant A i B (kadrowane kafle o równej szerokości) oraz wersja 1 (ukryta galeria).

Kod do skopiowania

Całość w jeden moduł Kod, razem ze znacznikami.

<style>
/* =====================================================
   SLAJDER PRODUKTOW v3 (wariant C) - sama Galeria Divi
   Klasa na module Galeria:  sp3-slajder
   Powiekszenie: wbudowane w Divi (jak w wariancie A).

   Roznica wobec v2: kafle maja WSPOLNA WYSOKOSC, ale
   szerokosc kazdy swoja - zdjecie widac w calosci, nic nie
   jest kadrowane. Bez napisu z boku i bez przyciemnienia.
   ===================================================== */

.sp3-slajder{
  --sp3-wysokosc: 260px;     /* wspolna wysokosc wszystkich kafli */
  --sp3-odstep: 30px;        /* przerwa miedzy kaflami */
  --sp3-przerwa: 3000;       /* co ile ms nastepny krok */
  --sp3-przejscie: 1500;     /* jak dlugo trwa krok */
  --sp3-powiekszenie: 1.05;  /* o ile rosnie kafel po najechaniu */
  --sp3-zolty: #F0C609;

  /* WIELKOSC PRZESUWU. Dwa sposoby zapisu:
       1     - o jeden kafel (2, 3 ... o tyle kafli)
       50%   - o polowe szerokosci okna galerii
       100%  - o cale okno, czyli "strona po stronie"
     Przy zapisie procentowym slajder przesuwa sie o tyle KAFLI, ile
     trzeba, zeby pokryc zadana czesc okna - dzieki temu zawsze konczy
     na krawedzi kafla i nic nie zostaje przeciete. */
  --sp3-przesuw: 1;

  overflow: hidden;
  padding-bottom: 26px;      /* miejsce na kafel rosnacy w dol */
}

/* GOTOWA NASTAWA: przesuw strona po stronie.
   Klasy na module Galeria: sp3-slajder sp3-strona
   Dluzszy skok wymaga wolniejszego przejazdu i dluzszego postoju,
   inaczej slajder niemal caly czas jest w ruchu. */
.sp3-slajder.sp3-strona{
  --sp3-przesuw: 100%;
  --sp3-przejscie: 2200;
  --sp3-przerwa: 4500;
}

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

/* kafel bierze szerokosc ze swojego zdjecia */
.sp3-slajder .et_pb_gallery_item{
  flex: 0 0 auto !important;
  width: auto !important;
  margin: 0 !important;
  padding: 0 !important;
  float: none !important;
  clear: none !important;
  position: relative;
  overflow: hidden;
  transform-origin: top center;
  transition: transform .5s cubic-bezier(.2,0,.2,1), box-shadow .5s ease;
}
.sp3-slajder .et_pb_gallery_item:hover{
  transform: scale(var(--sp3-powiekszenie));
  z-index: 5;
  box-shadow: 0 0 20px 0 rgba(0,0,0,.35);
}

/* wspolna wysokosc, szerokosc z proporcji pliku - zdjecie w calosci */
.sp3-slajder .et_pb_gallery_image{
  display: block;
  position: relative;
  overflow: hidden;
  height: var(--sp3-wysokosc);
  margin: 0 !important;
}
.sp3-slajder .et_pb_gallery_image a{ display:block; height:100%; position:relative; }
.sp3-slajder .et_pb_gallery_image img{
  display: block;
  height: var(--sp3-wysokosc);
  width: auto;
  max-width: none;
}

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

/* zolty kwadrat + dwie kreski jadace od krawedzi = plus */
.sp3-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(--sp3-zolty),var(--sp3-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;
}
.sp3-slajder .et_pb_gallery_item:hover .et_pb_gallery_image a::before{
  opacity:1;
  background-position:50% 50%, 50% 50%, 50% 50%;
}

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

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

@media (max-width:990px){ .sp3-slajder{ --sp3-wysokosc:210px; } }
@media (max-width:590px){ .sp3-slajder{ --sp3-wysokosc:170px; --sp3-odstep:15px; } }
</style>

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

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

      var styl = getComputedStyle(root);
      var PRZERWA   = parseFloat(styl.getPropertyValue('--sp3-przerwa'))   || 3000;
      var PRZEJSCIE = parseFloat(styl.getPropertyValue('--sp3-przejscie')) || 1500;
      var PRZESUW   = (styl.getPropertyValue('--sp3-przesuw') || '1').trim() || '1';

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

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

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

      /* Kafle maja rozne szerokosci, wiec krok NIE jest stala liczba pikseli.
         Celem jest zawsze lewa krawedz kafla o danym numerze. */
      function pozycja(i){
        return tor.children[i].offsetLeft - tor.children[0].offsetLeft;
      }
      function jednostka(){ return pozycja(oryg); }   /* dlugosc jednego kompletu */
      function ease(p){ return 1 - Math.pow(1 - p, 3); }

      function normalizuj(){
        while(idx >= oryg){ idx -= oryg; offset -= jednostka(); }
        while(idx < 0){ idx += oryg; offset += jednostka(); }
      }

      /* Ile kafli przeskoczyc w jednym kroku.
         Zapis liczbowy - tyle kafli. Zapis procentowy - tyle kafli,
         ile trzeba, zeby przejechac zadana czesc okna galerii. */
      function ileKafli(){
        if(PRZESUW.charAt(PRZESUW.length - 1) === '%'){
          var cel = root.clientWidth * (parseFloat(PRZESUW) || 100) / 100;
          var start = pozycja(idx);
          var n = 1;
          while(n < oryg && pozycja(idx + n) - start < cel) n++;
          return n;
        }
        return Math.min(oryg, Math.max(1, parseInt(PRZESUW, 10) || 1));
      }

      function przesun(kierunek, czas){
        if(tween){ offset = tween.to; idx = tween.idx; tween = null; normalizuj(); }
        var ile = ileKafli();
        while(idx + kierunek * ile < 0){   /* cofamy sie z poczatku - przeskakujemy o komplet */
          offset += jednostka();
          idx += oryg;
        }
        var nowy = idx + kierunek * ile;
        tween = { from: offset, to: pozycja(nowy), start: performance.now(), dur: czas, idx: nowy };
      }

      strzalki.querySelector('.sp3-prev').addEventListener('click', function(){ przesun(-1, 450); doNastepnego = PRZERWA; });
      strzalki.querySelector('.sp3-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; idx = tween.idx; tween = null;
            normalizuj();
            doNastepnego = PRZERWA;
          } else {
            offset = tween.from + (tween.to - tween.from) * ease(p);
          }
        } else if(!najechane && !bezRuchu){
          doNastepnego -= dt;
          if(doNastepnego <= 0) przesun(1, PRZEJSCIE);
        }

        tor.style.transform = 'translateX(' + (-offset) + 'px)';
        requestAnimationFrame(klatka);
      }
      requestAnimationFrame(klatka);
    });
  }
  if(document.readyState === 'loading') document.addEventListener('DOMContentLoaded', start);
  else start();
})();
</script>