→ לכל המאגר

G57 · גלגל תמונות שמסתובב בגלילה

GSAP GSAP · ScrollTrigger scrub תמונות וגלריותכרטיסיםתהליך ושלבים ממתין

תמונות מסודרות על היקף מעגל ענק שרובו מחוץ למסך. הגלילה מסובבת את הגלגל, כל תמונה עולה לראש הקשת בתורה, וכל אחת נשארת ישרה כלפי הצופה.

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

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

הדמו

העבודות שלנו

גלול. הגלגל מסתובב והפריטים עולים בתורם.

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

אותו מנגנון עובד גם עם שמות שנים או שלבי תהליך במקום תמונות, וגם בכיוון ההפוך.

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

קוד להדבקה

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

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

CSS

/* מחלקות עזר של המאגר שהרכיב הזה משתמש בהן */
.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-f{background:linear-gradient(160deg,#c92a2a,#ff8787)}

/* G57 · גלגל תמונות שמסתובב בגלילה */
.wh{height:300vh;position:relative}
.wh-stick{position:sticky;top:0;height:100vh;overflow:hidden;display:grid;place-items:center}
.wh-title{position:absolute;top:12vh;inset-inline:0;text-align:center;pointer-events:none;z-index:2}
.wh-title h3{margin:0;font-size:clamp(24px,3.4vw,46px)}
.wh-title p{margin:8px 0 0;color:var(--muted, #6a6d85);font-size:15px}
/* הגלגל גדול מהמסך בכוונה: רואים רק את הקשת העליונה שלו */
.wh-wheel{position:absolute;top:58vh;left:50%;width:min(190vh,190vw);aspect-ratio:1;
  transform:translate(-50%,0);will-change:transform}
.wh-item{position:absolute;top:0;left:50%;width:clamp(120px,15vh,220px);aspect-ratio:3/4;
  margin-inline-start:calc(clamp(120px,15vh,220px) / -2);
  transform-origin:50% calc(min(190vh,190vw) / 2)}
/* שכבה פנימית אחת שמסובבת-נגד. סיבוב נפרד של התמונה ושל התווית מסובב כל אחת
   סביב מרכז אחר, והתווית שיושבת בתחתית נשברת החוצה מהמסגרת. */
.wh-inner{position:absolute;inset:0;border-radius:14px;overflow:hidden;will-change:transform}
.wh-inner .ph{position:absolute;inset:0;border-radius:0;font-size:0}
.wh-inner b{position:absolute;inset-inline:0;bottom:0;padding:8px 10px;color:#fff;font-size:13px;
  background:linear-gradient(transparent,rgba(0,0,0,.6))}
.wh-after{padding:14vh var(--gutter, clamp(24px, 5vw, 96px));max-width:min(680px,92vw);margin-inline:auto;text-align:center;
  color:var(--muted, #6a6d85);font-size:17px;line-height:1.9}

HTML

<div class="wh"><div class="wh-stick">
  <div class="wh-title"><h3>העבודות שלנו</h3><p>גלול. הגלגל מסתובב והפריטים עולים בתורם.</p></div>
  <div class="wh-wheel">
    <div class="wh-item"><div class="wh-inner"><div class="ph ph-a"></div><b>משרד עורכי דין</b></div></div>
    <div class="wh-item"><div class="wh-inner"><div class="ph ph-c"></div><b>מותג קוסמטיקה</b></div></div>
    <div class="wh-item"><div class="wh-inner"><div class="ph ph-d"></div><b>קורס דיגיטלי</b></div></div>
    <div class="wh-item"><div class="wh-inner"><div class="ph ph-e"></div><b>פורטל לקוחות</b></div></div>
    <div class="wh-item"><div class="wh-inner"><div class="ph ph-b"></div><b>קליניקה פרטית</b></div></div>
    <div class="wh-item"><div class="wh-inner"><div class="ph ph-f"></div><b>יבואן ריהוט</b></div></div>
    <div class="wh-item"><div class="wh-inner"><div class="ph ph-a"></div><b>סטודיו צילום</b></div></div>
    <div class="wh-item"><div class="wh-inner"><div class="ph ph-c"></div><b>רשת מסעדות</b></div></div>
  </div>
</div></div>
<p class="wh-after">אותו מנגנון עובד גם עם שמות שנים או שלבי תהליך במקום תמונות, וגם בכיוון ההפוך.</p>

JS

gsap.registerPlugin(ScrollTrigger);

(function(){
  const wheel=document.querySelector(".wh-wheel");
  const items=gsap.utils.toArray(".wh-item");
  const reduce=matchMedia("(prefers-reduced-motion: reduce)").matches;
  const SPREAD=13;                                   // מעלות בין פריט לפריט
  const START=(items.length-1)*SPREAD/2;

  const angles=items.map((el,i)=>{
    const a=-START+i*SPREAD;
    gsap.set(el,{rotation:a});
    return a;
  });

  if(reduce){items.forEach((el,i)=>gsap.set(el.querySelector(".wh-inner"),{rotation:-angles[i]}));return;}

  // ההטיה האמיתית של פריט היא הזווית שלו על הגלגל ועוד סיבוב הגלגל עצמו, וזה משתנה
  // לאורך כל הגלילה. סיבוב-נגד סטטי מיישר אותו רק בנקודה אחת ומטה אותו בכל השאר,
  // ולכן שני הסיבובים יושבים על אותו טיימליין ומתקזזים בכל רגע.
  const tl=gsap.timeline({scrollTrigger:{trigger:".wh",start:"top top",end:"bottom bottom",scrub:.7}});
  tl.fromTo(wheel,{rotation:START},{rotation:-START,ease:"none",duration:1},0);
  items.forEach((el,i)=>{
    const a=angles[i];
    tl.fromTo(el.querySelector(".wh-inner"),
      {rotation:-(a+START)},{rotation:-(a-START),ease:"none",duration:1},0);
  });
})();