שיפורים בממשק ה-API של אנימציות באינטרנט ב-Chromium 84

תזמור אנימציות באמצעות הבטחות, שיפור ביצועים באמצעות אנימציות שניתן להחליף, אנימציות חלקות יותר באמצעות מצבי צבירה ועוד.

Kevin Ellis
Kevin Ellis

תאריך פרסום: 27 במאי 2020

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

Web Animations API הוא כלי שמאפשר למפתחים לכתוב אנימציות ציווייות באמצעות JavaScript. התוכן נכתב כדי לתמוך בהטמעות של אנימציה ובמעברים ב-CSS, וכדי לאפשר פיתוח של אפקטים עתידיים, כמו גם אפקטים קיימים להרכבת ולתזמון.

ב-Firefox וב-Safari כבר הטמיעו את כל התכונות של המפרט, אבל ב-Chromium 84 קיימים כבר מגוון תכונות שלא נתמכות בעבר ב-Chrome ו-Edge וכך מאפשרות פעולה הדדית בין דפדפנים.

Web Animations API נוסף ל-Chromium לראשונה בגרסה 36, ביולי 2014. עכשיו המפרט יושלם בגרסה 84, שתושקה ביולי 2020.
ההיסטוריה הארוכה של Web Animations API ב-Chromium.

תחילת העבודה

אם השתמשת בכללי @keyframe, יצירת אנימציה באמצעות Web Animations API אמורה להיות מוכרת מאוד. קודם צריך ליצור אובייקט של פריים מפתח. איך ייראה כך ב-CSS:

@keyframes openAnimation {
  0% {
    transform: scale(0);
  }
  100% {
    transform: scale(1);
  }
}

ייראה כך כאן ב-JavaScript:

const openAnimation = [
  { transform: 'scale(0)' },
  { transform: 'scale(1)' },
];

איפה מגדירים פרמטרים לאנימציה ב-CSS:

.modal {
  animation: openAnimation 1s 1 ease-in;
}

מגדירים ב-JS:

document.querySelector('.modal').animate(
    openAnimation, {
      duration: 1000, // 1s
      iterations: 1, // single iteration
      easing: 'ease-in' // easing function
    }
);

כמות הקוד דומה אבל ב-JavaScript, תקבלו כמה כוחות-על שאין לכם ב-CSS בלבד. היכולות האלה כוללות את היכולת ליצור רצף של אפקטים ושליטה רבה יותר במצבי ההפעלה שלהם.

מעבר ל-element.animate()

עם זאת, בעקבות העדכון הזה, Web Animations API כבר לא מוגבל לאנימציות שנוצרו באמצעות element.animate(). אנחנו יכולים גם לשנות אנימציות ומעברים ב-CSS.

getAnimations() היא שיטה שמחזירה את כל האנימציות ברכיב, בין אם הן נוצרו באמצעות element.animate() או באמצעות כללי CSS (אנימציה של CSS או מעבר). דוגמה למראה של הקוד:

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

איך לתזמר אנימציות באמצעות הבטחות

ב-Chromium 84 יש עכשיו שתי שיטות שבהן אפשר להשתמש עם הבטחות: animation.ready ו-animation.finished.

  • animation.ready מאפשרים לכם להמתין עד שהשינויים הממתינים ייכנסו לתוקף (כלומר, לעבור בין שיטות של בקרת הפעלה, כמו הפעלה והשהיה).
  • animation.finished מאפשר להריץ קוד JavaScript בהתאמה אישית כשהאנימציה מסתיימת.

ממשיכים עם הדוגמה שלנו ויוצרים שרשרת אנימציה מתואמת באמצעות animation.finished. כאן יש טרנספורמציה אנכית (scaleY), ואחריה טרנספורמציה אופקית (scaleX), ואחריה שינוי אטימות ברכיב צאצא:

מחילים טרנספורמציות ושקיפות על רכיב מודאלי פותח. הדגמה ב-Codepen
const transformAnimation = modal.animate(openModal, openModalSettings);
transformAnimation.finished.then(() => { text.animate(fadeIn, fadeInSettings)});

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

ב-CSS, היצירה מחדש יכולה להיות מסורבלת, במיוחד כאשר מחילים אנימציות ייחודיות אך עדיין ברצף על מספר רכיבים. צריך להשתמש ב-@keyframe, להגדיר את אחוז הזמן הנכון להצגת האנימציות ולהשתמש ב-animation-delay לפני הפעלת האנימציות בסדרה.

דוגמה: הפעלה, השהיה וחזרה אחורה

מה שאפשר לפתוח, צריך לסגור! למרבה המזל, מאז גרסת Chromium 39, ממשק Web Animations API מאפשר לנו להפעיל, להשהות ולהפוך את האנימציות שלנו.

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

דוגמה לפתיחה וסגירה של חלון בזמן לחיצה על הלחצן. לצפייה בהדגמה ב-Glitch

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

דוגמה: אינטראקציות דינמיות עם תמונות מפתח חלקיות

דוגמה לטירגוט מחדש, שבה לחיצה על העכבר משנה את האנימציה למיקום חדש. לצפייה בהדגמה של התקלה
selector.animate([{transform: `translate(${x}px, ${y}px)`}],
    {duration: 1000, fill: 'forwards'});

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

אפשר להפעיל מעברים חדשים בזמן שהמעברים הקיימים עדיין פועלים. פירוש הדבר הוא שהמעבר הנוכחי מופסק ונוצר מעבר חדש.

שיפור הביצועים באמצעות אנימציות שניתן להחליף

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

כשהעכבר זז, מוצגת אנימציה של נתיב כוכב שביט. לצפייה בהדגמה ב-Glitch
elem.addEventListener('mousemove', evt => {
  rectangle.animate(
    { transform: translate(${evt.clientX}px, ${evt.clientY}px) },
    { duration: 500, fill: 'forwards' }
  );
});

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

  1. האנימציה הסתיימה.
  2. יש אנימציה אחת או יותר במקום גבוה יותר בסדר האנימציות המורכבות שהסתיימה גם היא.
  3. האנימציות החדשות יוצרות אנימציה לאותם מאפיינים.

כדי לראות בדיוק כמה אנימציות מוחלפות, אפשר לספור את האנימציות שהוסרו באמצעות מונה, ולהשתמש ב-anim.onremove כדי להפעיל את המונה.

יש כמה שיטות נוספות לשפר עוד יותר את השליטה באנימציה:

  • animation.replaceState() מאפשר לעקוב אחרי הסטטוס של אנימציה: פעיל, קבוע או מושעה.
  • animation.commitStyles() מעדכן את הסגנון של רכיב על סמך הסגנון הבסיסי יחד עם כל האנימציות ברכיב לפי סדר מורכב.
  • animation.persist() מסמנת אנימציה כבלתי ניתנת להחלפה.

אנימציות חלקות יותר באמצעות מצבי שילוב

בעזרת Web Animations API, עכשיו אפשר להגדיר את המצב המשולב של האנימציות, כלומר הן יכולות להיות מצטברות או מצטברות, בנוסף למצב ברירת המחדל 'החלפה'. מצבים מורכבים מאפשרים למפתחים לכתוב אנימציות ייחודיות ולשלוט בשילוב של האפקטים. עכשיו יש תמיכה בשלושה מצבים מורכבים: 'replace' (מצב ברירת המחדל), 'add' ו-'accumulate'.

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

הדגמה שמציגה את מצבי ברירת המחדל, ההוספה והצבירה של מצבים מורכבים. לצפייה בהדגמה ב-Glitch

במצב המורכב 'replace' שמוגדר כברירת מחדל, האנימציה הסופית מחליפה את מאפיין הטרנספורמציה ומסתיימת ב-rotate(360deg) scale(1.4). עבור 'add', הפונקציה composite מוסיפה את הסיבוב ומכפילה את הגודל, וכתוצאה מכך מתקבל המצב הסופי rotate(720deg) scale(1.96). 'accumulate' משלבת את הטרנספורמציות, וכתוצאה מכך מתקבל rotate(720deg) scale(1.8). מידע נוסף על המורכבות של המצבים המשולבים האלה זמין ברשימות המניחים CompositeOperation ו-CompositeOperationOrAuto במפרט של Web Animations.

הנה דוגמה לרכיב ממשק משתמש:

תפריט נפתח עם תנועה קפיצית שבו הוחלו שתי אנימציות מורכבות. לצפייה בהדגמה ב-Glitch

כאן מוצגות שתי אנימציות top מורכבות. הראשונה היא אנימציית מאקרו, שמעבירה את התפריט הנפתח לגובה המלא של התפריט עצמו כאפקט של הסטה מראש הדף, והשנייה, מיקרו-אנימציה, מפעילה מעט עזיבה מהדף הראשון. שימוש במצב המורכב של 'add' מאפשר מעבר חלק יותר.

const dropDown = menu.animate(
    [
      { top: `${-menuHeight}px`, easing: 'ease-in' },
      { top: 0 }
    ], { duration: 300, fill: 'forwards' });

  dropDown.finished.then(() => {
    const bounce = menu.animate(
      [
        { top: '0px', easing: 'ease-in' },
        { top: '10px', easing: 'ease-out' },
        { ... }
      ], { duration: 300, composite: 'add' });
  });

מה צפוי בעתיד ל-Web Animations API

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