→ לכל המאגר

B62 · קרוסלת עומק תלת-ממדית

התנהגויות GSAP · pointer תמונות וגלריותכרטיסים ממתין

כרטיסים על מסילה תלת-ממדית: הפעיל קדימה ובגודל מלא, השאר נסוגים לעומק ומתכהים. גרירה, חיצים, מקלדת ואוטו-פליי שנעצר במגע.

מתי משתמשים: תיק עבודות, מוצרים נבחרים, סיפורי לקוח. שלושה עד שבעה פריטים, כשרוצים נוכחות קולנועית בלי גריד.

הדמו כאן עיצובי-ניטרלי בכוונה. כשהמהלך נכנס לפרויקט, מיובאת רק ההתנהגות: הצבעים, הרדיוסים, הפונטים והצללים יורשים את העיצוב של אותו פרויקט.

הדמו

1
קליניקה פרטית, אתר תדמית
2
חנות אונליין, 2,000 מוצרים
3
משרד עורכי דין, שלוש שפות
4
מערכת CRM לסוכנות
5
דף נחיתה לקורס
מקור: ReactBits DepthCarousel (GSAP). נכתב מחדש בלי ספריית גרירה: pointer events עם סף 40px, וכל כרטיס מקבל translateZ/x/opacity לפי המרחק מהפעיל. RTL: החץ הימני מוביל אחורה ומקש ArrowRight גם. במובייל הפרספקטיבה קטנה והכרטיסים צרים יותר כדי שהשכנים יציצו.

קוד להדבקה

הקוד כאן עומד בפני עצמו. כל טוקן עיצוב נושא ברירת מחדל, מחלקות העזר של המאגר מוטמעות בו, ותגי הסקריפט מסודרים לפי הסדר הנכון. פרויקט שמגדיר --accent או --line משלו יורש אותם אוטומטית. הרכיב הזה מסתמך על dir="rtl".

פתח קובץ עצמאי ↗
הצג את הקוד כאן

CSS

/* מחלקות עזר של המאגר שהרכיב הזה משתמש בהן */
.stage{padding:var(--sec, clamp(120px, 12vw, 260px)) var(--gutter, clamp(24px, 5vw, 96px));position:relative}
.stage.tight{padding-block:clamp(48px,5vw,90px)}
.stage.full{padding-inline:0}
.ph{display:flex;align-items:center;justify-content:center;color:#fff;font-weight:700;border-radius:var(--r, 14px)}
.ph-a{background:linear-gradient(160deg,#3b5bdb,#748ffc)}
.ph-b{background:linear-gradient(160deg,#5f3dc4,#9775fa)}
.ph-c{background:linear-gradient(160deg,#0b7285,#3bc9db)}
.ph-d{background:linear-gradient(160deg,#2b8a3e,#69db7c)}
.ph-e{background:linear-gradient(160deg,#e8590c,#ffa94d)}
.ph-l{background:rgba(16,18,43,.72);color:#fff;padding:.18em .7em;border-radius:999px;line-height:1.3;font-size:max(.78em,12px)}

/* B62 · קרוסלת עומק תלת-ממדית */
.dc{position:relative;height:clamp(360px,52vh,520px);perspective:1200px;overflow:hidden;touch-action:pan-y;cursor:grab;user-select:none}
.dc.drag{cursor:grabbing}
.dc-track{position:absolute;inset:0;transform-style:preserve-3d}
.dc-card{position:absolute;top:50%;left:50%;width:min(62vw,520px);aspect-ratio:16/10;margin:calc(min(62vw,520px) * -0.3125) 0 0 calc(min(62vw,520px) * -0.5);border-radius:var(--r, 14px);overflow:hidden;will-change:transform,opacity;box-shadow:0 30px 70px rgba(0,0,0,.25)}
.dc-card .ph{position:absolute;inset:0;border-radius:0;font-size:22px}
.dc-cap{position:absolute;inset-inline:0;bottom:0;padding:16px 18px;background:linear-gradient(transparent,rgba(0,0,0,.55));color:#fff;font-weight:600;font-size:15px}
.dc-nav{display:flex;justify-content:center;align-items:center;gap:14px;margin-top:18px}
.dc-btn{width:44px;height:44px;border-radius:50%;border:1px solid var(--line, #e4e4ee);background:var(--card, #fff);color:var(--ink, #16182b);font-size:18px;cursor:pointer;display:grid;place-items:center}
.dc-btn:hover{border-color:var(--ink, #16182b)}
.dc-btn:focus-visible{outline:2px solid var(--accent, #4a3aff);outline-offset:2px}
.dc-dots{display:flex;gap:6px}
.dc-dots i{width:6px;height:6px;border-radius:50%;background:var(--line, #e4e4ee);transition:transform .3s cubic-bezier(.2,.6,.2,1),background-color .3s}
.dc-dots i.on{background:var(--accent, #4a3aff);transform:scale(1.5)}
@media(max-width:767px){.dc{perspective:800px;height:min(60vh,420px)}.dc-card{width:74vw;margin:calc(74vw * -0.3125) 0 0 calc(74vw * -0.5)}}
@media (prefers-reduced-motion: reduce){.dc-dots i{transition:none}}

HTML

<div class="stage tight">
<div class="dc" aria-roledescription="carousel" aria-label="פרויקטים נבחרים" tabindex="0">
  <div class="dc-track">
    <figure class="dc-card"><div class="ph ph-a"><span class="ph-l">1</span></div><figcaption class="dc-cap">קליניקה פרטית, אתר תדמית</figcaption></figure>
    <figure class="dc-card"><div class="ph ph-b"><span class="ph-l">2</span></div><figcaption class="dc-cap">חנות אונליין, 2,000 מוצרים</figcaption></figure>
    <figure class="dc-card"><div class="ph ph-c"><span class="ph-l">3</span></div><figcaption class="dc-cap">משרד עורכי דין, שלוש שפות</figcaption></figure>
    <figure class="dc-card"><div class="ph ph-d"><span class="ph-l">4</span></div><figcaption class="dc-cap">מערכת CRM לסוכנות</figcaption></figure>
    <figure class="dc-card"><div class="ph ph-e"><span class="ph-l">5</span></div><figcaption class="dc-cap">דף נחיתה לקורס</figcaption></figure>
  </div>
</div>
<div class="dc-nav"><button class="dc-btn dc-prev" aria-label="הקודם">→</button><div class="dc-dots" aria-hidden="true"></div><button class="dc-btn dc-next" aria-label="הבא">←</button></div>
</div>

JS

(function(){
  const root=document.querySelector(".dc"),cards=[...root.querySelectorAll(".dc-card")],dots=document.querySelector(".dc-dots"),n=cards.length;
  const reduce=matchMedia("(prefers-reduced-motion: reduce)").matches,mob=matchMedia("(max-width:767px)").matches;
  const GAP=mob?.28:.34;   // כמה מהרוחב זז כל כרטיס הצידה; קטן במובייל כדי שהשכנים יציצו
  let cur=0,timer=null;
  cards.forEach(()=>dots.appendChild(document.createElement("i")));
  function layout(animate){
    const w=cards[0].offsetWidth;
    cards.forEach((c,i)=>{
      let off=i-cur; if(off>n/2)off-=n; if(off<-n/2)off+=n;      // מסילה מעגלית: הקצר מבין שני הכיוונים
      const depth=Math.abs(off);
      // RTL: הבא נמצא משמאל, לכן ההיסט האופקי הפוך
      gsap.to(c,{x:-off*w*GAP,z:-depth*220,opacity:depth>2?0:1-depth*.28,scale:1-depth*.06,zIndex:n-depth,duration:animate&&!reduce?.7:0,ease:"power3.out",overwrite:true});
      c.setAttribute("aria-hidden",off!==0);
    });
    [...dots.children].forEach((d,i)=>d.classList.toggle("on",i===cur));
  }
  function go(step){cur=(cur+step+n)%n;layout(true);restart();}
  function restart(){clearInterval(timer);if(!reduce)timer=setInterval(()=>go(1),3800);}
  document.querySelector(".dc-next").addEventListener("click",()=>go(1));
  document.querySelector(".dc-prev").addEventListener("click",()=>go(-1));
  // מקלדת: בעברית חץ שמאלה = הבא
  root.addEventListener("keydown",e=>{if(e.key==="ArrowLeft")go(1);if(e.key==="ArrowRight")go(-1);});
  // גרירה עם סף, בלי ספרייה
  let x0=null;
  root.addEventListener("pointerdown",e=>{x0=e.clientX;root.classList.add("drag");root.setPointerCapture(e.pointerId);clearInterval(timer);});
  root.addEventListener("pointerup",e=>{if(x0===null)return;const dx=e.clientX-x0;x0=null;root.classList.remove("drag");if(Math.abs(dx)>40)go(dx<0?1:-1);else restart();});
  root.addEventListener("pointercancel",()=>{x0=null;root.classList.remove("drag");restart();});
  root.addEventListener("mouseenter",()=>clearInterval(timer));root.addEventListener("mouseleave",restart);
  layout(false);restart();
  addEventListener("resize",()=>layout(false));
})();