→ לכל המאגר

G121 · יציאה מזום: מתמונה אחת לפסיפס שלם

GSAP GSAP · ScrollTrigger · pin תמונות וגלריותהירו ופתיחה ממתין

מתחילים צמוד לתמונה אחת שממלאת את המסך, והגלילה מרחיקה את המצלמה עד שמתגלה פסיפס של עשרים תמונות שהיא רק אחת מהן. "זה חלק ממשהו גדול".

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

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

הדמו

גלול למטה, הדמו מגיע ↓
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18

פרויקט אחד מתוך מאה ארבעים

וכל אחד מהם התחיל משיחה.

עוד מסלול גלילה. נסה גם חזרה למעלה ↑
הגריד כולו מקבל scale מ-X ל-1 עם transform-origin על התמונה הפותחת (מחושב מהמיקום שלה), כך שהיא נשארת במקום והשאר נכנסות מסביב. במובייל הזום ההתחלתי קטן יותר (הגריד צר) והפסיפס 3 עמודות.

קוד להדבקה

הקוד כאן עומד בפני עצמו. כל טוקן עיצוב נושא ברירת מחדל, מחלקות העזר של המאגר מוטמעות בו, ותגי הסקריפט מסודרים לפי הסדר הנכון. פרויקט שמגדיר --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-l{background:rgba(16,18,43,.72);color:#fff;padding:.18em .7em;border-radius:999px;line-height:1.3;font-size:max(.78em,12px)}

/* G121 · יציאה מזום: מתמונה אחת לפסיפס שלם */
.zo{position:relative;height:260vh}
.zo-pin{position:sticky;top:0;height:100vh;overflow:hidden;display:grid;place-items:center;background:var(--ink, #16182b)}
.zo-grid{display:grid;grid-template-columns:repeat(6,1fr);gap:10px;width:min(1100px,92vw);will-change:transform}
.zo-grid .ph{aspect-ratio:1;font-size:16px;border-radius:12px}
.zo-grid .ph.hero{outline:3px solid var(--accent, #4a3aff);outline-offset:-3px}
.zo-cap{position:absolute;bottom:6vh;inset-inline:0;text-align:center;color:var(--bg, #f7f7fa);opacity:0}
.zo-cap h2{margin:0 0 6px;font-size:var(--fs-h2, clamp(28px, 1.9vw + 0.5rem, 52px))}
.zo-cap p{margin:0;opacity:.75}
@media(max-width:767px){.zo-grid{grid-template-columns:repeat(3,1fr);gap:6px}}

HTML

<div class="zo">
  <div class="zo-pin"><div class="zo-grid">
    <div class="ph ph-a"><span class="ph-l">1</span></div><div class="ph ph-b"><span class="ph-l">2</span></div><div class="ph ph-c"><span class="ph-l">3</span></div><div class="ph ph-d"><span class="ph-l">4</span></div><div class="ph ph-e"><span class="ph-l">5</span></div><div class="ph ph-a"><span class="ph-l">6</span></div>
    <div class="ph ph-b"><span class="ph-l">7</span></div><div class="ph ph-c"><span class="ph-l">8</span></div><div class="ph ph-d hero"><span class="ph-l">9</span></div><div class="ph ph-e"><span class="ph-l">10</span></div><div class="ph ph-a"><span class="ph-l">11</span></div><div class="ph ph-b"><span class="ph-l">12</span></div>
    <div class="ph ph-c"><span class="ph-l">13</span></div><div class="ph ph-d"><span class="ph-l">14</span></div><div class="ph ph-e"><span class="ph-l">15</span></div><div class="ph ph-a"><span class="ph-l">16</span></div><div class="ph ph-b"><span class="ph-l">17</span></div><div class="ph ph-c"><span class="ph-l">18</span></div>
  </div><div class="zo-cap"><h2>פרויקט אחד מתוך מאה ארבעים</h2><p>וכל אחד מהם התחיל משיחה.</p></div></div>
</div>

JS

gsap.registerPlugin(ScrollTrigger);

(function(){
  const grid=document.querySelector(".zo-grid"),hero=grid.querySelector(".hero"),pin=document.querySelector(".zo-pin");
  if(matchMedia("(prefers-reduced-motion: reduce)").matches){gsap.set(".zo-cap",{opacity:1});return;}
  // המדידה נעשית ב-onRefreshInit, כשהגריד נקי מטרנספורם. לא בתוך פונקציית ערך של הטווין:
  // gsap.set על אותו אלמנט באמצע בניית הטווין שיבש את מטמון הטרנספורם (scale רק לרוחב, נמדד).
  let start=8;
  function measure(){gsap.set(grid,{clearProps:"transform"});const g=grid.getBoundingClientRect(),h=hero.getBoundingClientRect();
    grid.style.transformOrigin=(h.left-g.left+h.width/2)+"px "+(h.top-g.top+h.height/2)+"px";
    start=Math.max(pin.clientWidth/h.width,pin.clientHeight/h.height)*1.05;}
  measure();
  const tl=gsap.timeline({scrollTrigger:{trigger:".zo",start:"top top",end:"bottom bottom",scrub:.7,invalidateOnRefresh:true,onRefreshInit:measure}});
  tl.fromTo(grid,{scale:()=>start},{scale:1,duration:1,ease:"power2.inOut"})
    .to(".zo-cap",{opacity:1,duration:.2},.75);
})();