שורה נעה באתר: היכן שאנימציית marquee שוברת את הפריסה ואת הקשב
שורה נעה כמעט תמיד מתחילה בשורת CSS אחת — וכמעט תמיד מסתיימת בתלונה "בטלפון זה מקרטע, ובמחשב זה זז מהר מדי". הסיבה היא שאנימציית marquee קובעת היסט בפיקסלים לפרק זמן קבוע: רוחב התוכן במסכים שונים שונה, ואילו משך הזמן זהה, ולכן מהירות התנועה משתנה פי כמה.
בנוסף, @keyframes קלאסי עם translateX(-100%) יוצר טלטלה בנקודת החיבור של המחזור — הטקסט "קופץ" לנקודת ההתחלה, והעין קולטת זאת באופן מיידי.
קבוצת הבעיות השנייה — התנגשות עם גלילה. שורה שנעה מעל בלוק נגלל חוטפת מחוות במכשירי מגע, ואילו overflow: hidden על ההורה חותך אלמנטים נפתחים ושובר את הפריסה בשינוי גודל.
אם האנימציה מופעלת על מאפיין שגורם לחישוב מחדש של הפריסה, הדפדפן מצייר מחדש את השכבה בכל פריים — במכשירים חלשים זה ירידה בקצב הפריימים והתחממות, ובעמודים ארוכים גם מאבק על ה-thread הראשי עם טעינה עצלה של תמונות.
בנפרד — נגישות וקשב. טקסט שנע ללא הפסקה מפריע לקרוא תוכן סמוך, מבלבל את הפוקוס בניווט עם המקלדת ואינו נעצר לפי ההגדרה המערכתית "צמצם תנועה". משתמש עם רגישות וסטיבולרית פשוט עוזב את העמוד, ומנועי חיפוש רואים כפילות טקסט אם השורה משוכפלת לצורך רציפות.
מפרקים כיצד זה נסגר: חישוב מהירות לפי רוחב התוכן, מחזור רציף ללא טלטלה, השהיה במעבר עכבר ובהגדרות מערכת, העברת האנימציה לשכבה קומפוזיציונית — כדי שהשורה תישאר חלקה ולא תגזול משאבים משאר העמוד.
מימוש אנימציית marquee: CSS, JavaScript ומצבי תנועה
שורת טקסט רצה ב-CSS טהור נבנית על @keyframes עם translateX(-50%) ותוכן משוכפל: העתק של הרצועה ניצב צמוד למקור, והמחזור נסגר בלי קפיצה. גרסה כזו לא נוגעת ב-thread הראשי — האנימציה עוברת ל-compositing, ו-will-change: transform מסיר רינדורים מיותרים.
החלקות נקבעת לא לפי משך אלא לפי עקביות: linear בלי הפסקות נותן תנועה אחידה, ו-animation-play-state: paused על :hover עוצר את הרצועה בלי לחשב מחדש את המיקום.
כשהתוכן מגיע מבחוץ או שהרצועה נבנית דינמית, marquee css מפנה מקום לשורה רצה ב-JavaScript. כאן המיקום מחושב דרך requestAnimationFrame, ולא דרך setInterval: הפריימים מסונכרנים עם קצב רענון המסך, ובמכשירים חלשים לא מצטברת סחיפה.
את המהירות מחזיקים בפיקסלים לשנייה וממירים להיסט לפי הזמן שחלף — בעת ירידת פריימים הרצועה לא "מקרטעת", אלא פשוט נעה בצורה אחידה יותר.
מצבי תנועה והמחיר שלהם
גלילה רציפה — המצב הבסיסי: מהירות אחת, מחזור אחד, עומס צפוי. פינג-פונג (הרצועה מגיעה לקצה ומתהפכת) דורש שינוי כיוון בגבול — אם לא מחליקים אותו, בהיפוך נראית מיקרו-קפיצה.
השהיה במעבר עכבר ובפוקוס מהמקלדת — הכרחית לנגישות: התנועה מושבתת דרך prefers-reduced-motion, והרצועה נשארת קריאה באופן סטטי.
| וריאנט | מתי מתאים | מגבלות |
|---|---|---|
CSS @keyframes + שכפול הרצועה |
סט סטטי של לוגואים, טיקר פשוט | התוכן קבוע בשלב הבנייה, קשה לשנות מהירות תוך כדי |
JavaScript + requestAnimationFrame |
נתונים דינמיים, מהירות משתנה, השהיה לפי אירועים | נדרש פיקוח על ניקוי פריימים בעת פירוק |
| היברידי: אנימציית CSS הנשלטת על ידי משתנים | נדרשת חלקות של CSS וגמישות בהגדרות | דורש סנכרון קפדני של משתני CSS עם המצב |
מה זה נותן בסופו של דבר
ברצועה ארוכה של עשרות אלמנטים, המעבר ל-compositing ול-requestAnimationFrame מסיר ירידות פריימים בזמן גלילת העמוד: התנועה מפסיקה להתחרות ב-thread הראשי.
רצועה של מאה לוגואים שומרת על מהלך אחיד, והשהיה במעבר עכבר והשבתה דרך prefers-reduced-motion סוגרות את דרישות הנגישות בלי מימוש נפרד.
לשליטה בפריימים ולסנכרון עם הסימון בפרויקטים אנו משתמשים ב-requestAnimationFrame — הוא נותן תזמון צפוי במקום טיימרים.
איך אנחנו מטמיעים שורת טקסט רצה: מביקורת ועד מסירה
הטמעת אנימציית marquee מתחילה בביקורת של המבנה הנוכחי: בודקים ממה מורכב הבלוק, האם יש רוחבים קבועים, איך מתנהג הקונטיינר בעת הצפה והאם הסרטה החדשה מתנגשת עם טרנספורמציות ו-overflow קיימים.
בשלב הזה מזהים היכן האנימציה מותרת לפי דרישות הנגישות, והיכן יהיה צורך להשבית אותה או להחליף אותה בבלוק סטטי — זה זול יותר מאשר לעשות מחדש לאחר השחרור.
בהמשך מתכננים את סכמת התנועה: בוחרים בין אנימציית CSS עם שכפול תוכן ללולאה ללא תפרים לבין שליטה דרך TypeScript, כאשר נדרשים השהיה במעבר עכבר, מהירות אדפטיבית או סנכרון עם גלילה.
בנפרד מחשבים את אורך המסלול ומספר ההעתקים, כדי שמסכים רחבים לא ייווצרו רווחים, ובמסכים צרים הסרטה לא "תרעד" בגלל חישוב מחדש של הרוחב.
- ביקורת מבנה ותוכן — פירוק מבנה הבלוק, בדיקת הצפות והתנגשויות עם סגנונות.
- תכנון סכמת האנימציה — בחירת גישה, חישוב אורך המסלול ומספר העותקים של התוכן.
- הגדרת שורת טקסט רצה — קישור המהירות לרוחב הקונטיינר, השהיה במעבר עכבר ובפוקוס.
- התחשבות בנגישות — השבתת תנועה כאשר
prefers-reduced-motion, שמירה על קריאות הטקסט. - בדיקה במכשירים — הרצה במובייל, טאבלטים ודסקטופ, כולל שינוי כיוון מסך.
- אופטימיזציה של הרינדור — העברת האנימציה לשכבת קומפוזיציה, כדי שהסרטה לא תגרום לחישוב מחדש של הפריסה.
- מסירה ותיעוד — פרמטרים של הגדרה, הוראות לתיקון מהירות והרכב הסרטה.
רצף כזה מסיר תקלות אופייניות: טלטולים במכשירים חלשים, קטיעת הסרטה בעת שינוי רוחב חלון ו"היתקעות" האנימציה לאחר חזרה ללשונית. במקום תיקונים לאחר תלונות, הלקוח מקבל התנהגות צפויה של הבלוק על כל מגוון המכשירים. תארו את המשימה — נחזור עם הערכה ותוכנית עבודה.
במה שונה אנימציית CSS מיישום JavaScript של שורת טקסט נעה?
אנימציית CSS של שורת טקסט נעה חיה כולה במנוע הרינדור של הדפדפן: אתם מגדירים פריימים מפתחיים ו-animation-play-state, ואת היסט האלמנטים מחשב ה-compositor, תוך עקיפת ה-thread הראשי.
כל עוד השורה אינה משנה תוכן, הדף אינו מחשב מחדש את ה-layout ואינו מצייר מחדש שכבות — עם עשרה בלוקים כאלה בדף זה מורגש ביציבות הגלילה.
המגבלה היא באותו מקום: CSS אינו יודע כמה מקום תופס התוכן, ולכן לולאה ללא תפר נבנית על שכפול המסלול ו-translateX(-50%), והשהיה במעבר עכבר או בפוקוס נעשית רק דרך :hover ו-:focus-within.
יישום JavaScript מנהל את האנימציה מהקוד: המיקום מחושב ב-requestAnimationFrame, ההיסט נכתב ל-transform, והמהירות, הכיוון ונקודת ההתחלה הם משתנים רגילים.
זה נותן את מה שאין ל-CSS: עצירה על אלמנט מסוים, סנכרון של כמה מסלולים, התאמת המהירות לאורך הטקסט לאחר טעינת נתונים, השהיה כאשר הלשונית עוברת לרקע דרך visibilitychange.
המחיר — לולאה ב-thread הראשי: אם בסמוך מתבצעת עבודה כבדה, הפריימים נופלים, ובכתיבה לא זהירה ל-left או top במקום ל-transform הדפדפן מחשב מחדש את ה-layout בכל פריים.
הבחירה ביישום שורת טקסט נעה תלויה בתרחיש. סט סטטי של לוגואים או טיקר טקסטואלי ללא אינטראקטיביות — CSS: פחות קוד, אפס עומס על ה-thread, עובד גם כאשר סקריפטים מושבתים.
פיד עם נתונים מ-API, באורכי אלמנטים שונים, עם עצירה בלחיצה וחיבור לבלוקים אחרים — JavaScript, כי ניהול אנימציה דורש גישה למצב, ולא רק לסגנונות.
היברידי מכסה את שני המקרים: את ה-layout ואת התנועה הבסיסית מגדיר CSS, והסקריפט מתערב נקודתית — מציב animation-play-state: paused במעבר עכבר, מחשב מחדש את משך הזמן לאחר טעינת תוכן נוסף, ומשבית תנועה כאשר prefers-reduced-motion. כך הלולאה הראשית נשארת ב-compositor, והלוגיקה — בקוד.
מקרה בוחן: כיצד שורת טקסט רצה הפסיקה לזעזע ולהעמיס על העמוד
לקוח הגיע עם תלונה טיפוסית: שורת טקסט רצה בעמוד הראשי "מזדעזעת" במחשבים ניידים חלשים ובטלפונים ישנים, ובפתיחת לשונית ברקע הדפדפן מעמיס על המעבד עד כדי התחממות מורגשת.
המדידות אישרו את האבחנה: האנימציה פעלה דרך setInterval עם חישוב מחדש של המיקום ב-JavaScript, כל 16 מ"ש נקרא offsetWidth ונכתב style.left — הדבר גרם לסנכרון כפוי של הפריסה בכל פריים. במכשיר חלש המרווח לא הספיק לפעול, פריימים דולגו, והסרט נע בדילוגים חזותיים כדי להדביק את המיקום.
מה שינו
העבירו את התנועה לשכבת הקומפוזיטור: אנימציה דרך transform: translate3d() עם פריימים מרכזיים ב-CSS, ואת ה-JS השאירו רק לניהול — השהיה במעבר עכבר, כוונון מהירות והפעלה מחדש. משך הזמן מוגדר במשתנה, ולא בחישוב מחדש בלולאה, ולכן הדפדפן מפעיל את הסרט בעצמו, ללא מעורבות ה-thread הראשי. שכפול התוכן לשם רציפות נעשה דרך עותק aria-hidden, כדי שקורא המסך לא יקרא את הטקסט פעמיים.
הוספו prefers-reduced-motion: כאשר ההגדרה המערכתית היא "פחות תנועה", הסרט נעצר והופך לאזור גלילה רגיל. השהיה דרך visibilitychange משתיקה את האנימציה בלשונית לא פעילה — הדבר הסיר את ההתחממות ואת צריכת הסוללה המיותרת.
| היה | стало |
|---|---|
| חישוב מחדש של המיקום ב-JS בכל פריים | אנימציית CSS על הקומפוזיטור |
| דילוגים במכשירים חלשים | תנועה חלקה, מהירות יציבה |
| התחממות וצריכת סוללה ברקע | האנימציה מושהית מחוץ ללשונית |
| הטקסט נקרא פעמיים | העותק מוסתר מקוראי מסך |
תוצאה
לאחר התיקונים קצב הפריימים במכשיר חלש התיישר והפסיק לצנוח, העומס על המעבד במנוחה ירד בערך פי שלושה, ובלשונית ברקע — כמעט לאפס. מהירות הסרט כעת זהה בכל המכשירים, מכיוון שהיא נקבעת לפי זמן המעבר, ולא לפי קצב הרינדור מחדש. התיקונים ארכו מחזור עבודה אחד ולא דרשו שינוי של סימון העמוד.
הגישה נשענת על מנגנונים סטנדרטיים של הדפדפן, ולכן אינה נשברת בעת עדכונים. פרטי המימוש הוצלבו עם התיעוד MDN Web Docs בנושא אנימציות CSS וקומפוזיטינג.
ספקות לפני הטמעה: תאימות, שחזור וגישות
מה אם השורה הנעה מתנגשת עם הפריסה הנוכחית? אנימציית Marquee כמעט תמיד מבודדת בשכבה נפרדת: מכולה עם overflow: hidden, מסלול על טרנספורמציות, שכפול תוכן ללולאה ללא תפרים.
בלוק כזה לא נוגע ברשת העמוד, לא משפיע על זרימת המסמך ולא שובר רכיבים קיימים — תאימות האנימציה נבדקת נקודתית, ללא בנייה מחדש של הפריסה.
איך מבוצע שחזור שינויים אם משהו ישתבש? כל תיקון נכנס כקומיט נפרד עם תיאור, הסגנונות והסקריפט מועברים לקבצים מבודדים ולא מפוזרים על פני הפרויקט. שחזור שינויים הוא החזרת קומיט אחד, ולא התרת קוד זר ידנית: הגרסה הקודמת של הבלוק מוחזרת תוך דקות.
מי ואיך מקבל גישות לפיתוח? אנחנו עובדים דרך חשבונות נפרדים עם ההרשאות המינימליות הנדרשות, ללא גישה לנתוני תשלום ומידע אישי. גישות לפיתוח ניתנות לתקופת העבודה ונשללות עם סיומה — זה מתועד במסירה.
האנימציה תגמגם במכשירים חלשים?
התנועה מבוססת על טרנספורמציות ועל requestAnimationFrame, ולא על חישוב מחדש של המיקום בכל פריים. כאשר prefers-reduced-motion מופעל, השורה הנעה נעצרת אוטומטית — זו דרישת נגישות, ולא אופציה. במכשירים חלשים העומס נשאר בגבולות שכבה מרוכבת אחת.
לאחר המסירה הלקוח מקבל לידיו:
- קוד המקור של הבלוק עם הערות ומבנה הקבצים;
- קונפיגורציה של מהירות, כיוון ועצירה בריחוף;
- תרשים חיבור לתבנית ולמערכת הבנייה הנוכחית;
- כללי נגישות והתנהגות במצב reduced-motion;
- הוראות לעריכת טקסט והוספת אלמנטים חדשים;
- צ'קליסט בדיקה לאחר עדכון תוכן;
- תיאור הליך השחזור לגרסה הקודמת.
תארו את המשימה ואת הסטאק הנוכחי — נחזור עם הערכה ותוכנית עבודה לשורה הנעה.
לוחות זמנים ליישום שורת טקסט נעה לפי שלבים
לוחות הזמנים אינם תלויים באנימציית ה-marquee עצמה — המכניקה שלה אורכת שעות — אלא בכמה זמן לוקח לתאם את התוכן, לקבל גישות לסביבה ולבדוק על נתונים אמיתיים.
להלן תוכנית פיתוח משוערת, שאנו מדייקים לאחר ניתוח המשימה: המספרים מציגים את סדר העבודות ואת הנקודות שבהן בדרך כלל נוצרת המתנה מצד הלקוח.
במקביל מתנהלת עבודה עם התוכן: אם השורה שואבת נתונים ממקור חיצוני, מתווסף זמן לתיאום הפורמט ולטיפול בתקלות.
בשלב הבדיקה אנו בוחנים בנפרד כיצד מתנהגת אנימציית ה-marquee כאשר הטקסט גדול מרוחב המסך פי כמה, וכאשר הוא כמעט ואינו קיים — אלה שני המקרים הנפוצים ביותר שבהם אב הטיפוס נראה טוב, אך בפרודקשן נשברת הפריסה.
אומדן כללי למשימה טיפוסית — משבוע עד שבועיים וחצי, אם אין צורך באינטגרציה עם מערכות חיצוניות. לוחות זמנים מדויקים ליישום נמסור לאחר שנראה את העמוד שלכם ונבין מהיכן מגיע הטקסט. תארו את המשימה — נחזור עם הערכה ותוכנית לפי שלבים.







