מראה למבקר איפה הוא נמצא בסיפור של הדף, בלי סרגל התקדמות יבש. התחנות נדלקות כשהסמן מגיע אליהן, והיעד נפתח בסוף. מתאים לדף שמספר תהליך: הרשמה, הסעה, שלבי עבודה. פס מסע בגלילה הוא רכיב מתוך רכיבים של כליקס. מה שמורידים הוא רכיב שלם, גם כ-HTML עצמאי וגם כרכיב React. אין ספרייה חיצונית, אין שלב בנייה, ואם כליקס ייעלם מחר הקוד ימשיך לעבוד.
רכיב · גלילה · בלי ספריות · מחשב בלבד
<!-- פס מסע בצד המסך — להדביק לפני </body> -->
<div id="klix-rail" aria-hidden="true"></div>
<style>
#klix-rail{position:fixed;top:0;bottom:0;inset-inline-end:.75rem;width:4rem;z-index:40;
pointer-events:none;color:#e2e8f0}
/* בטלפון הפס גוזל רוחב ולא מוסיף מידע */
@media (max-width:1023px){ #klix-rail{display:none} }
#klix-rail .rail-line{position:absolute;inset-inline-start:50%;transform:translateX(-50%);
width:1px;display:flex;flex-direction:column;align-items:center;
justify-content:space-around;opacity:.3}
#klix-rail .rail-line span{width:1px;height:.5rem;border-radius:999px;background:currentColor}
#klix-rail .rail-node{position:absolute;inset-inline-start:50%;
display:flex;align-items:center;justify-content:center;border-radius:999px;
transition:opacity .5s ease,transform .5s ease}
#klix-rail .rail-stop{width:1.75rem;height:1.75rem;font-size:.75rem;background:#f8fafc;
box-shadow:0 1px 4px rgba(0,0,0,.25);opacity:.25;transform:translate(-50%,-50%) scale(.6)}
#klix-rail .rail-stop.on{opacity:1;transform:translate(-50%,-50%) scale(1)}
#klix-rail .rail-end{width:2.5rem;height:2.5rem;font-size:1.15rem;background:#fbbf24;
box-shadow:0 6px 18px rgba(0,0,0,.3);opacity:.3;transform:translate(-50%,-50%) scale(.7)}
#klix-rail .rail-end.on{opacity:1;transform:translate(-50%,-50%) scale(1)}
#klix-rail .rail-marker{position:absolute;inset-inline-start:50%;font-size:1.5rem;
transform:translate(-50%,-50%);filter:drop-shadow(0 2px 3px rgba(0,0,0,.35))}
</style>
<script>
(function () {
// ═══ ערכו כאן ═══
var MARKER = '🚗';
var DESTINATION = '🎊';
var STOPS = [
{ at: 0.18, icon: '👤', label: 'נרשמתם' },
{ at: 0.40, icon: '👥', label: 'הצטרפו אליכם' },
{ at: 0.60, icon: '👨👩👦', label: 'המשפחה בדרך' }
];
var FROM = 14; // תחילת המסלול, באחוזי גובה מסך
var TO = 74; // סופו
var TRAVEL = 0.7; // באיזו התקדמות גלילה מגיעים ליעד
// ═════════════════
var rail = document.getElementById('klix-rail');
if (!rail) return;
// מיקום על הפס עבור סף גלילה נתון. כך התחנה יושבת בדיוק במקום שבו
// הסמן נמצא כשהסף נחצה, ולא במספר קבוע שנבחר בעין.
function posAt(at) { return FROM + (TO - FROM) * Math.min(1, at / TRAVEL); }
var line = document.createElement('div');
line.className = 'rail-line';
line.style.top = FROM + 'vh';
line.style.bottom = (100 - TO) + 'vh';
for (var i = 0; i < 12; i++) line.appendChild(document.createElement('span'));
rail.appendChild(line);
var nodes = STOPS.map(function (stop) {
var el = document.createElement('div');
el.className = 'rail-node rail-stop';
el.style.top = posAt(stop.at) + 'vh';
el.textContent = stop.icon;
if (stop.label) el.title = stop.label;
rail.appendChild(el);
return el;
});
var end = document.createElement('div');
end.className = 'rail-node rail-end';
end.style.top = TO + 'vh';
end.textContent = DESTINATION;
rail.appendChild(end);
var marker = document.createElement('div');
marker.className = 'rail-marker';
marker.textContent = MARKER;
rail.appendChild(marker);
// מיקום הסמן נגזר ישירות מהגלילה, כלומר מתנועה של המשתמש עצמו.
// לכן אין כאן מה לכבות במצב תנועה מופחתת — אין תנועה אוטומטית.
var pending = false;
function read() {
pending = false;
var max = document.documentElement.scrollHeight - window.innerHeight;
var p = max > 0 ? Math.min(1, Math.max(0, window.scrollY / max)) : 0;
marker.style.top = posAt(p) + 'vh';
for (var i = 0; i < nodes.length; i++) {
nodes[i].classList.toggle('on', p >= STOPS[i].at);
}
end.classList.toggle('on', p >= TRAVEL);
}
function onScroll() {
if (pending) return;
pending = true;
requestAnimationFrame(read);
}
read();
window.addEventListener('scroll', onScroll, { passive: true });
window.addEventListener('resize', onScroll);
})();
</script>