סרטון שלא מתנגן לבד: הגלילה היא שמזיזה אותו קדימה ואחורה, פריים אחרי פריים. עוצרים והוא עוצר, גוללים אחורה והוא חוזר.
מתי משתמשים: הדגמת מוצר מכל הזוויות, תהליך ייצור, לפני ואחרי, אנימציית מותג. הרגע שבו המבקר מבין שהוא שולט במה שקורה על המסך.
הדמו כאן עיצובי-ניטרלי בכוונה. כשהמהלך נכנס לפרויקט, מיובאת רק ההתנהגות: הצבעים, הרדיוסים, הפונטים והצללים יורשים את העיצוב של אותו פרויקט.
בסרטון אמיתי זה יהיה מוצר שמסתובב, מכונה שעובדת או בית שנבנה. הקובץ חייב להיות מקודד לחיפוש מהיר, אחרת התנועה מקרטעת.
הקוד כאן עומד בפני עצמו. כל טוקן עיצוב נושא ברירת מחדל, מחלקות העזר של המאגר מוטמעות בו, ותגי הסקריפט מסודרים לפי הסדר הנכון. פרויקט שמגדיר --accent או --line משלו יורש אותם אוטומטית. הרכיב הזה מסתמך על dir="rtl".
/* G49 · וידאו שמתקדם עם הגלילה */
.vs{position:relative;height:320vh}
.vs-stick{position:sticky;top:0;height:100vh;display:grid;place-items:center;overflow:hidden;background:#0e0f16}
.vs-video{width:min(92vw,960px);border-radius:var(--r, 14px);display:block}
.vs-cap{position:absolute;inset-inline:0;bottom:11vh;text-align:center;color:#fff;pointer-events:none}
.vs-cap b{display:block;font-size:clamp(22px,3.4vw,42px);margin-bottom:6px}
.vs-cap span{font-size:15px;opacity:.75}
/* מוטבע ומעוגל. רצועה בגובה 4 פיקסלים שנמתחת מקצה לקצה המסך נקראת כקו מפריד תועה
ולא כמד התקדמות, במיוחד כשהיא ריקה בתחילת הגלילה. */
.vs-bar{position:absolute;inset-inline:clamp(16px,6vw,80px);bottom:clamp(14px,2.5vh,26px);
height:5px;border-radius:999px;overflow:hidden;background:rgba(255,255,255,.14)}
.vs-bar i{display:block;height:100%;width:0;border-radius:999px;background:var(--accent, #4a3aff)}
.vs-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}
<div class="vs">
<div class="vs-stick">
<video class="vs-video" src="https://liavwebdesign-spec.github.io/motion-vault/assets/media/scrub.mp4" poster="https://liavwebdesign-spec.github.io/motion-vault/assets/media/scrub.jpg"
muted playsinline preload="auto" aria-label="הדגמה: וידאו שמתקדם עם הגלילה"></video>
<div class="vs-cap"><b>הגלילה היא הנגן</b><span>גלול למטה ולמעלה. הסרטון עוקב.</span></div>
<div class="vs-bar"><i></i></div>
</div>
</div>
<p class="vs-after">בסרטון אמיתי זה יהיה מוצר שמסתובב, מכונה שעובדת או בית שנבנה. הקובץ חייב להיות מקודד לחיפוש מהיר, אחרת התנועה מקרטעת.</p>
gsap.registerPlugin(ScrollTrigger);
(function(){
const v=document.querySelector(".vs-video"),bar=document.querySelector(".vs-bar i");
const reduce=matchMedia("(prefers-reduced-motion: reduce)").matches;
let target=0,current=0,dur=0,ready=false;
function start(){
dur=v.duration||0; if(!dur)return;
ready=true;
ScrollTrigger.create({
trigger:".vs",start:"top top",end:"bottom bottom",scrub:true,
onUpdate:self=>{target=self.progress*dur;bar.style.width=(self.progress*100).toFixed(1)+"%";}
});
ScrollTrigger.refresh();
}
v.addEventListener("loadedmetadata",start);
if(v.readyState>=1)start();
// iOS לא מרשה קפיצה בזמן לפני שהווידאו התנגן פעם אחת, ולכן דוחפים play ומיד pause
const kick=()=>{const p=v.play();if(p&&p.then)p.then(()=>v.pause()).catch(()=>{});};
addEventListener("touchstart",kick,{once:true,passive:true});
addEventListener("click",kick,{once:true});
// החלקה: מתקרבים אל היעד בהדרגה במקום להציב currentTime בכל אירוע גלילה
gsap.ticker.add(()=>{
if(!ready)return;
if(reduce){v.currentTime=target;return;}
current+=(target-current)*.18;
if(Math.abs(target-current)<.004)current=target;
if(v.seeking)return;
v.currentTime=current;
});
})();