→ לכל המאגר

G94 · מצלמה שנוסעת על תמונה בגלילה

GSAP GSAP · ScrollTrigger · pin תמונות וגלריותתהליך ושלבים ממתין

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

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

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

הדמו

גלול למטה, הדמו מגיע ↓
תחנה 01

הכניסה הראשית

מפה מקבלת פנים, מרחב המתנה, וקפה.

תחנה 02

חדרי הטיפול

שישה חדרים, אור טבעי בכולם.

תחנה 03

הגינה האחורית

איפה שהמטופלים אוהבים לחכות.

עוד מסלול גלילה. נסה גם חזרה למעלה ↑
כל תחנה היא אחוזים על התמונה + זום. הטרנספורם מחושב בזמן refresh לפי גודל המכל (invalidateOnRefresh), לכן זה נכון בכל רוחב. במובייל הזום גדול יותר כי המסך צר, והכיתוב יושב למטה במקום בצד.

קוד להדבקה

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

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

CSS

/* מחלקות עזר של המאגר שהרכיב הזה משתמש בהן */
.ph{display:flex;align-items:center;justify-content:center;color:#fff;font-weight:700;border-radius:var(--r, 14px)}

/* G94 · מצלמה שנוסעת על תמונה בגלילה */
.cam{position:relative;height:320vh}
.cam-pin{position:sticky;top:0;height:100vh;overflow:hidden;background:var(--ink, #16182b)}
.cam-img{position:absolute;inset:0;transform-origin:0 0;will-change:transform}
.cam-img .ph{position:absolute;inset:0;border-radius:0;font-size:0;background:linear-gradient(135deg,#1b2a4a,#2f7a6b 45%,#c98a3a)}
.cam-spot{position:absolute;width:26px;height:26px;border-radius:50%;background:var(--accent, #4a3aff);box-shadow:0 0 0 8px color-mix(in srgb,var(--accent, #4a3aff) 30%,transparent);translate:-50% -50%}
.cam-cap{position:absolute;inset-inline-end:clamp(20px,5vw,60px);bottom:clamp(20px,6vh,60px);max-width:34ch;background:color-mix(in srgb,var(--bg, #f7f7fa) 92%,transparent);color:var(--ink, #16182b);padding:18px 22px;border-radius:16px;opacity:0;translate:0 12px}
.cam-cap b{display:block;font-size:12px;color:var(--accent, #4a3aff);letter-spacing:.14em;margin-bottom:6px}
.cam-cap h3{margin:0 0 6px;font-size:20px}
.cam-cap p{margin:0;color:var(--muted, #6a6d85);font-size:15px;line-height:1.6}
@media(max-width:767px){.cam-cap{inset-inline:16px;max-width:none}}

HTML

<div class="cam">
  <div class="cam-pin">
    <div class="cam-img"><div class="ph"></div>
      <span class="cam-spot" style="left:22%;top:34%"></span><span class="cam-spot" style="left:64%;top:28%"></span><span class="cam-spot" style="left:48%;top:72%"></span></div>
    <div class="cam-cap" data-i="0"><b>תחנה 01</b><h3>הכניסה הראשית</h3><p>מפה מקבלת פנים, מרחב המתנה, וקפה.</p></div>
    <div class="cam-cap" data-i="1"><b>תחנה 02</b><h3>חדרי הטיפול</h3><p>שישה חדרים, אור טבעי בכולם.</p></div>
    <div class="cam-cap" data-i="2"><b>תחנה 03</b><h3>הגינה האחורית</h3><p>איפה שהמטופלים אוהבים לחכות.</p></div>
  </div>
</div>

JS

gsap.registerPlugin(ScrollTrigger);

(function(){
  const pin=document.querySelector(".cam-pin"),img=document.querySelector(".cam-img"),caps=gsap.utils.toArray(".cam-cap");
  const stops=[{x:.22,y:.34},{x:.64,y:.28},{x:.48,y:.72}];
  const Z=()=>matchMedia("(max-width:767px)").matches?3.2:2.4;
  if(matchMedia("(prefers-reduced-motion: reduce)").matches){gsap.set(caps[0],{opacity:1,translate:"0 0"});return;}
  // מביאים נקודה (אחוזים) למרכז המסך בזום נתון: x = W/2 - px*W*z
  const to=(p)=>({x:()=>pin.clientWidth/2-p.x*pin.clientWidth*Z(),y:()=>pin.clientHeight/2-p.y*pin.clientHeight*Z(),scale:Z});
  const tl=gsap.timeline({scrollTrigger:{trigger:".cam",start:"top top",end:"bottom bottom",scrub:.8,invalidateOnRefresh:true}});
  stops.forEach((p,i)=>{
    tl.to(img,{...to(p),duration:1.2,ease:"power2.inOut"},i*2)
      .to(caps[i],{opacity:1,translate:"0 0",duration:.3},i*2+1)
      .to(caps[i],{opacity:0,translate:"0 -12px",duration:.3},i*2+1.7);
  });
  tl.to(img,{x:0,y:0,scale:1,duration:1.2,ease:"power2.inOut"},stops.length*2);   // חזרה למבט מלא בסוף
})();