פריצה של מצב אנימציה של CSS וזמן השמעה PlatoBlockchain Data Intelligence. חיפוש אנכי. איי.

מצב הנפשת CSS פריצה וזמן השמעה

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

השראה הדגמה של FPS ועוד אחת Wolfenstein CodePen, החלטתי לבנות גרסה משלי. הוא מבוסס באופן רופף על פרק 1 - קומה 9 של משחק התלת מימד המקורי של Wolfenstein.

עורך: המשחק הזה דורש תגובה מהירה בכוונה כדי להימנע ממסך Game Over.

הנה סרטון משחק:

[תוכן מוטבע]

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

:checked ~ div { animation-name: spin; }

הסביבה מורכבת מפרצופי רשת תלת מימדיים והאנימציות הן לרוב תרגומים וסיבובים תלת מימדיים רגילים. שום דבר לא ממש מפואר.

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

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

ברמה הטכנית, זה אומר:

  • הפעל מחדש אנימציה כאשר תיבת הסימון הבאה מסומנת.
  • האט את האנימציה, כאשר תיבת סימון מסומנת.

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

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

  • הרבה דוגמאות אינטראקטיביות.
  • ניתוחים: איך כל דוגמה עובדת (או לא עובדת)?
  • מאחורי הקלעים: איך דפדפנים מטפלים במצבי אנימציה?

תן לי "לזרוק את הלבנים שלי".

בעיה 1: הפעלה חוזרת של אנימציה

הדוגמה הראשונה: "רק עוד תיבת סימון"

האינטואיציה הראשונה שלי הייתה "פשוט הוסף תיבת סימון נוספת", מה שלא עובד:

כל תיבת סימון פועלת בנפרד, אך לא שתיהן יחד. אם תיבת סימון אחת כבר מסומנת, השנייה כבר לא פועלת.

כך זה עובד (או "לא עובד"):

  1. אל האני animation-name of <div> is none כברירת מחדל.
  2. המשתמש לוחץ על תיבת סימון אחת, animation-name הופך להיות spin, והאנימציה מתחילה מההתחלה.
  3. לאחר זמן מה, המשתמש לוחץ על תיבת הסימון השנייה. כלל CSS חדש נכנס לתוקף, אבל animation-name is עוד spin, כלומר לא הוספה או הוסרה אנימציה. האנימציה פשוט ממשיכה להתנגן כאילו כלום לא קרה.

הדוגמה השנייה: "שיבוט האנימציה"

גישת עבודה אחת היא לשכפל את האנימציה:

#spin1:checked ~ div { animation-name: spin1; }
#spin2:checked ~ div { animation-name: spin2; }

הנה איך זה עובד:

  1. animation-name is none בתחילה.
  2. המשתמש לוחץ על "ספין!", animation-name הופך להיות spin1. האנימציה spin1 הוא התחיל מההתחלה כי זה עתה נוסף.
  3. המשתמש לוחץ על "סובב שוב!", animation-name הופך להיות spin2. האנימציה spin2 הוא התחיל מההתחלה כי זה עתה נוסף.

שימו לב שבשלב מס' 3, spin1 מוסר בגלל סדר כללי ה-CSS. זה לא יעבוד אם "סובב שוב!" נבדק תחילה.

הדוגמה השלישית: "הוספת אותה אנימציה"

גישת עבודה נוספת היא "להוסיף את אותה אנימציה":

#spin1:checked ~ div { animation-name: spin; }
#spin2:checked ~ div { animation-name: spin, spin; }

זה דומה לדוגמא הקודמת. אתה באמת יכול להבין את ההתנהגות כך:

#spin1:checked ~ div { animation-name: spin1; }
#spin2:checked ~ div { animation-name: spin2, spin1; }

שימו לב שכאשר "סובב שוב!" מסומן, האנימציה הישנה הפועלת הופכת לאנימציה השנייה ברשימה החדשה, מה שעלול להיות לא אינטואיטיבי. תוצאה ישירה היא: הטריק לא יעבוד אם animation-fill-mode is forwards. הנה הדגמה:

אם אתה תוהה מדוע זה המקרה, הנה כמה רמזים:

  • animation-fill-mode is none כברירת מחדל, כלומר "לאנימציה אין השפעה כלל אם היא לא משחקת".
  • animation-fill-mode: forwards; פירושו "לאחר שהאנימציה תסתיים, היא חייבת להישאר בפריים המפתח האחרון לנצח".
  • spin1ההחלטה של ​​תמיד מתעלמת spin2זה בגלל spin1 מופיע מאוחר יותר ברשימה.
  • נניח שהמשתמש לוחץ על "ספין!", ממתין לסיבוב מלא, ואז לוחץ על "ספין שוב!". ברגע זה. spin1 הוא כבר גמור, ו spin2 רק מתחיל.

דיון

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

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

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

בעיה 2: הילוך איטי

אפשר לחשוב שהאטה של ​​אנימציה היא רק עניין של הגדרת זמן ארוך יותר animation-duration:

div { animation-duration: 0.5s; }
#slowmo:checked ~ div { animation-duration: 1.5s; }

אכן, זה עובד:

… או שכן?

עם כמה שינויים, זה אמור להיות קל יותר לראות את הבעיה.

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

יהיה קל יותר להבין זאת אם נציג שני "אלמנטים צללים":

שני רכיבי הצל מריצים את אותן אנימציות עם שונות animation-duration. והם אינם מושפעים מתיבת הסימון.

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

ציטוט המפרט של W3C:

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

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

עוד ניסיון

רעיון אחד הוא להשהות את האנימציה הנוכחית, ואז להוסיף אנימציה איטית יותר שמשתלטת משם:

div {
  animation-name: spin1;
  animation-duration: 2s;
}

#slowmo:checked ~ div {
  animation-name: spin1, spin2;
  animation-duration: 2s, 5s;
  animation-play-state: paused, running;
}

אז זה עובד:

… או שכן?

זה מאט כאשר אתה לוחץ על "Slowmo!". אבל אם תחכה למעגל שלם, תראה "קפיצה". למעשה, זה תמיד קופץ למצב כאשר "Slowmo!" נלחץ על.

הסיבה היא שאין לנו א from keyframe מוגדר - ואנחנו לא צריכים. כאשר המשתמש לוחץ על "Slowmo!", spin1 מושהה בעמדה כלשהי, ו spin2 מתחיל בדיוק באותו מיקום. אנחנו פשוט לא יכולים לחזות את העמדה הזו מראש... או שאנחנו יכולים?

פתרון עובד

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

div {
  transform: rotate(var(--angle1));
  animation-name: spin1;
  animation-duration: 2s;
}

#slowmo:checked ~ div {
  transform: rotate(var(--angle2));
  animation-name: spin1, spin2;
  animation-duration: 2s, 5s;
  animation-play-state: paused, running;
}

@keyframes spin1 {
  to {
    --angle1: 360deg;
  }
}

@keyframes spin2 {
  from {
    --angle2: var(--angle1);
  }
  to {
    --angle2: calc(var(--angle1) + 360deg);
  }
}

הערה: @property משמש בדוגמה זו, כלומר לא נתמך על ידי כל הדפדפנים.

הפתרון "המושלם".

יש אזהרה לפתרון הקודם: "יציאה מ-slowmo" לא עובדת טוב.

הנה פתרון טוב יותר:

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

פתרון זה עובד כמו "החלפת" "הילוכים":

  • הילוכים: יש שניים <div>ס. האחד הוא ההורה של השני. לשניהם יש את spin אנימציה אבל עם שונה animation-duration. המצב הסופי של האלמנט הוא הצטברות של שתי האנימציות.
  • הסטות: בהתחלה, רק אחד <div> האנימציה שלו פועלת. השני מושהה. כאשר תיבת הסימון מופעלת, שתי האנימציות מחליפות את מצבן.

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

פתרון מעשי (עם JS)

עבור אנימציות כלליות, אפשר להשיג את פונקציית ההילוך האיטי עם קצת JavaScript:

הסבר מהיר:

  • מאפיין מותאם אישית משמש למעקב אחר התקדמות האנימציה.
  • האנימציה "מופעלת מחדש" כאשר תיבת הסימון מוחלפת.
  • קוד JS מחשב את הנכון animation-delay כדי להבטיח מעבר חלק. אני ממליץ את המאמר הזה אם אינך מכיר ערכים שליליים של animation-delay.

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

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

הערה צדדית: במקור, ניסיתי גם ליצור גרסת CSS בלבד אך לא הצלחתי. למרות שלא בטוח ב-100%, אני חושב שזה בגלל animation-delay is לא ניתן להנפשה.

הנה גרסה עם JavaScript מינימלי. רק "כניסה לסלומו" עובדת.

אנא הודע לי אם אתה מצליח ליצור גרסת CSS עובדת בלבד!

כל אנימציה בהילוך איטי (עם JS)

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

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

לדוגמה:

  • animation-timing-function לא linear.
  • animation-direction לא normal.
  • ערכים מרובים ב animation-name עם שונה animation-durationזה ו animation-delay.

גם שיטה זו מתוארת כאן עבור API של אנימציות אינטרנט.

תודות

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

בהתחלה, לא היה לי מושג איך הם נוצרו. מאוחר יותר קראתי ולמדתי ממנו הרבה ההדרכה הנחמדה הזו מאת אנה טודור.

כפי שהתברר, בנייה והנפשה של אובייקטים תלת מימדיים עם CSS אינם שונים בהרבה מביצוע זה מַמחֶה, רק עם טעם קצת שונה.

סיכום

במאמר זה בחנו את ההתנהגות של אנימציות CSS כאשר א animate-* הנכס משתנה. במיוחד פיתחנו פתרונות ל"השמעה חוזרת של אנימציה" ו"אנימציה איטית".

אני מקווה שתמצא את המאמר הזה מעניין. בבקשה תן לי לדעת את המחשבות שלך!

בול זמן:

עוד מ טריקים של CSS