→ לכל המאגר

G111 · מסך טלפון מוצמד עם מסכים שמתחלפים

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

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

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

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

הדמו

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

מסך הבית

מה קרה היום, במבט אחד. בלי לחפור.

02

לידים בזמן אמת

כל פנייה נכנסת לרשימה עם מקור, שעה ומי מטפל.

03

דוח שבועי

כמה נכנס, כמה נסגר, ומה פתוח. נשלח לבד.

עוד מסלול גלילה. נסה גם חזרה למעלה ↑
המסכים ערומים בתוך המסגרת ומוזזים ב-yPercent, כך שאין reflow. במובייל הטלפון מוצמד למעלה בגובה 46vh והטקסט גולל מתחת, כמו בשאר מהלכי הצמדה במאגר.

קוד להדבקה

הקוד כאן עומד בפני עצמו. כל טוקן עיצוב נושא ברירת מחדל, מחלקות העזר של המאגר מוטמעות בו, ותגי הסקריפט מסודרים לפי הסדר הנכון. פרויקט שמגדיר --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}

/* G111 · מסך טלפון מוצמד עם מסכים שמתחלפים */
.pm{display:grid;grid-template-columns:1fr 1fr;gap:var(--gap, clamp(16px, 1.6vw, 36px));align-items:start;max-width:1000px;margin-inline:auto}
.pm-pin{position:sticky;top:0;height:100vh;display:grid;place-items:center}
.pm-phone{position:relative;width:min(280px,64vw);aspect-ratio:9/19;border-radius:38px;background:var(--ink, #16182b);padding:12px;box-shadow:0 40px 80px rgba(0,0,0,.25)}
.pm-phone::before{content:"";position:absolute;top:22px;left:50%;translate:-50% 0;width:34%;height:22px;border-radius:12px;background:var(--ink, #16182b);z-index:3}
.pm-screen{position:relative;width:100%;height:100%;border-radius:28px;overflow:hidden;background:var(--card, #fff)}
.pm-s{position:absolute;inset:0;padding:54px 18px 18px;display:grid;align-content:start;gap:12px;background:var(--card, #fff)}
.pm-s i{display:block;border-radius:10px;background:var(--bg, #f7f7fa);border:1px solid var(--line, #e4e4ee);height:16px}
.pm-s i.big{height:120px;background:var(--accent, #4a3aff);border:0;opacity:.85}
.pm-s i.mid{height:64px}
.pm-s b{font-size:15px;color:var(--ink, #16182b)}
.pm-steps{display:flex;flex-direction:column;gap:44vh;padding-block:36vh}
.pm-step{opacity:.3;transition:opacity .4s}
.pm-step.on{opacity:1}
.pm-step b{display:block;font-size:12px;color:var(--accent, #4a3aff);letter-spacing:.14em;margin-bottom:8px}
.pm-step h3{margin:0 0 8px;font-size:clamp(22px,2.6vw,36px)}
.pm-step p{margin:0;color:var(--muted, #6a6d85);line-height:1.7;max-width:36ch}
@media(max-width:767px){.pm{grid-template-columns:1fr}.pm-pin{height:46vh;top:0;z-index:2;background:var(--bg, #f7f7fa)}.pm-phone{width:min(40vw,190px)}.pm-steps{gap:26vh;padding-block:6vh 30vh}}

HTML

<div class="stage tight"><div class="pm">
  <div class="pm-pin"><div class="pm-phone"><div class="pm-screen">
    <div class="pm-s"><b>היום</b><i class="big"></i><i></i><i></i><i class="mid"></i></div>
    <div class="pm-s"><b>לידים</b><i class="mid"></i><i class="mid"></i><i class="mid"></i><i></i></div>
    <div class="pm-s"><b>דוחות</b><i></i><i class="big"></i><i></i><i></i><i></i></div>
  </div></div></div>
  <div class="pm-steps">
    <div class="pm-step"><b>01</b><h3>מסך הבית</h3><p>מה קרה היום, במבט אחד. בלי לחפור.</p></div>
    <div class="pm-step"><b>02</b><h3>לידים בזמן אמת</h3><p>כל פנייה נכנסת לרשימה עם מקור, שעה ומי מטפל.</p></div>
    <div class="pm-step"><b>03</b><h3>דוח שבועי</h3><p>כמה נכנס, כמה נסגר, ומה פתוח. נשלח לבד.</p></div>
  </div>
</div></div>

JS

gsap.registerPlugin(ScrollTrigger);

(function(){
  const screens=gsap.utils.toArray(".pm-s"),steps=gsap.utils.toArray(".pm-step"),reduce=matchMedia("(prefers-reduced-motion: reduce)").matches;
  screens.forEach((s,i)=>gsap.set(s,{yPercent:i?100:0}));
  let cur=0;
  function go(k){if(k===cur)return;const dir=k>cur?1:-1,prev=cur;cur=k;
    steps.forEach((s,i)=>s.classList.toggle("on",i===k));
    if(reduce){gsap.set(screens[prev],{yPercent:100});gsap.set(screens[k],{yPercent:0});return;}
    gsap.to(screens[prev],{yPercent:-100*dir,duration:.6,ease:"power3.inOut"});
    gsap.fromTo(screens[k],{yPercent:100*dir},{yPercent:0,duration:.6,ease:"power3.inOut"});}
  steps[0].classList.add("on");
  steps.forEach((s,i)=>ScrollTrigger.create({trigger:s,start:"top 60%",end:"bottom 40%",onEnter:()=>go(i),onEnterBack:()=>go(i)}));
})();