הרבה ממשקי Animation API מקבלים בדרך כלל פרמטרים להתאמה אישית של ההתנהגות שלהם.
התאמה אישית של אנימציות באמצעות הפרמטר AnimationSpec
רוב ממשקי ה-Animation API מאפשרים למפתחים להתאים אישית את מפרטי האנימציה באמצעות פרמטר AnimationSpec אופציונלי.
val alpha: Float by animateFloatAsState( targetValue = if (enabled) 1f else 0.5f, // Configure the animation duration and easing. animationSpec = tween(durationMillis = 300, easing = FastOutSlowInEasing), label = "alpha" )
יש סוגים שונים של AnimationSpec ליצירת סוגים שונים של אנימציה.
יצירת אנימציה שמבוססת על פיזיקה באמצעות spring
הפונקציה spring יוצרת אנימציה שמבוססת על פיזיקה בין ערכי ההתחלה והסיום. היא מקבלת 2 פרמטרים: dampingRatio ו-stiffness.
dampingRatio מגדיר את מידת הקפיציות של הקפיץ. ערך ברירת המחדל הוא
Spring.DampingRatioNoBouncy.
stiffness מגדיר את המהירות שבה הקפיץ צריך לנוע לעבר ערך הסוף. ערך ברירת המחדל הוא Spring.StiffnessMedium.
val value by animateFloatAsState( targetValue = 1f, animationSpec = spring( dampingRatio = Spring.DampingRatioHighBouncy, stiffness = Spring.StiffnessMedium ), label = "spring spec" )
הפונקציה spring יכולה לטפל בהפסקות בצורה חלקה יותר מאשר סוגי AnimationSpec שמבוססים על משך זמן, כי היא מבטיחה את המשכיות המהירות כשערך היעד משתנה במהלך האנימציות. spring משמש כברירת המחדל של AnimationSpec בהרבה ממשקי API של אנימציה, כמו animate*AsState ו-updateTransition.
לדוגמה, אם נחיל הגדרת spring על האנימציה הבאה שמופעלת על ידי מגע של המשתמש, כשנפריע לאנימציה בזמן שהיא מתקדמת, אפשר לראות שהשימוש ב-tween לא מגיב בצורה חלקה כמו השימוש ב-spring.
tween לעומת spring לאנימציה, והפסקת האנימציה.הנפשה בין ערכי ההתחלה והסיום עם עקומת האטה באמצעות tween
הפונקציה tween יוצרת אנימציה בין ערכי ההתחלה והסיום בdurationMillis שצוין באמצעות עקומת האטה. tween הוא קיצור של המילה "בין" באנגלית (between) – כי הוא עובר בין שני ערכים.
אפשר גם לציין delayMillis כדי לדחות את תחילת האנימציה.
val value by animateFloatAsState( targetValue = 1f, animationSpec = tween( durationMillis = 300, delayMillis = 50, easing = LinearOutSlowInEasing ), label = "tween delay" )
מידע נוסף זמין במאמר בנושא האטה.
אנימציה לערכים ספציפיים בתזמונים מסוימים באמצעות keyframes
keyframes יוצר אנימציה על סמך ערכי תמונת המצב שצוינו בחותמות זמן שונות במהלך האנימציה. בכל רגע נתון, ערך האנימציה יחושב באמצעות אינטרפולציה בין שני ערכים של פריים מרכזי. לכל אחד מפרימי המפתח האלה, אפשר לציין את המאפיין Easing כדי לקבוע את עקומת האינטרפולציה.
לא חובה לציין את הערכים ב-0 אלפיות השנייה ובמשך הזמן. אם לא מציינים את הערכים האלה, הם מוגדרים כברירת מחדל לערכי ההתחלה והסיום של האנימציה, בהתאמה.
val value by animateFloatAsState( targetValue = 1f, animationSpec = keyframes { durationMillis = 375 0.0f at 0 using LinearOutSlowInEasing // for 0-15 ms 0.2f at 15 using FastOutLinearInEasing // for 15-75 ms 0.4f at 75 // ms 0.4f at 225 // ms }, label = "keyframe" )
אנימציה חלקה בין פריימים מרכזיים באמצעות keyframesWithSplines
כדי ליצור אנימציה שעוקבת אחרי עקומה חלקה כשהיא עוברת בין ערכים, אפשר להשתמש במפרטי אנימציה מסוג keyframesWithSplines במקום keyframes.
val offset by animateOffsetAsState( targetValue = Offset(300f, 300f), animationSpec = keyframesWithSpline { durationMillis = 6000 Offset(0f, 0f) at 0 Offset(150f, 200f) atFraction 0.5f Offset(0f, 100f) atFraction 0.7f } )
פריים מפתח מבוסס-ספליין שימושי במיוחד לתנועה דו-ממדית של פריטים במסך.
בסרטונים הבאים מוצגים ההבדלים בין keyframes לבין keyframesWithSpline, בהינתן אותה קבוצה של קואורדינטות x ו-y שמעגל צריך לעקוב אחריהן.
keyframes
|
keyframesWithSplines
|
|---|---|
כמו שאפשר לראות, הקיפריימים שמבוססים על עקומות מציעים מעברים חלקים יותר בין הנקודות, כי הם משתמשים בעקומות בזייה כדי ליצור אנימציה חלקה בין הפריטים. המפרט הזה שימושי לאנימציה מוגדרת מראש. עם זאת, אם אתם עובדים עם נקודות שמונעות על ידי המשתמש, עדיף להשתמש בקפיצים כדי להשיג חלקות דומה בין הנקודות, כי אפשר להפריע להן.
חזרה על אנימציה עם repeatable
repeatable מפעיל אנימציה שמבוססת על משך זמן (כמו tween או keyframes) שוב ושוב עד שהוא מגיע למספר החזרות שצוין. אפשר להעביר את הפרמטר repeatMode כדי לציין אם האנימציה צריכה לחזור על עצמה מההתחלה (RepeatMode.Restart) או מהסוף (RepeatMode.Reverse).
val value by animateFloatAsState( targetValue = 1f, animationSpec = repeatable( iterations = 3, animation = tween(durationMillis = 300), repeatMode = RepeatMode.Reverse ), label = "repeatable spec" )
חזרה על אנימציה בלי הפסקה באמצעות infiniteRepeatable
infiniteRepeatable דומה ל-repeatable, אבל חוזר על עצמו מספר אינסופי של פעמים.
val value by animateFloatAsState( targetValue = 1f, animationSpec = infiniteRepeatable( animation = tween(durationMillis = 300), repeatMode = RepeatMode.Reverse ), label = "infinite repeatable" )
בבדיקות שמשתמשות ב-ComposeTestRule, אנימציות שמשתמשות ב-infiniteRepeatable לא פועלות. הרכיב יעובד באמצעות הערך הראשוני של כל ערך מונפש.
מעבר מיידי לערך הסופי באמצעות snap
snap הוא AnimationSpec מיוחד שמעביר מיד את הערך לערך הסופי. אפשר לציין delayMillis כדי לעכב את תחילת האנימציה.
val value by animateFloatAsState( targetValue = 1f, animationSpec = snap(delayMillis = 50), label = "snap spec" )
הגדרת פונקציית החלקה בהתאמה אישית
פעולות AnimationSpec שמבוססות על משך זמן (כמו tween או keyframes) משתמשות ב-Easing כדי לשנות את החלק של האנימציה. כך ערך האנימציה יכול להאיץ ולהאט, במקום לנוע בקצב קבוע. השבר הוא ערך בין 0 (התחלה) ל-1.0 (סוף) שמציין את הנקודה הנוכחית באנימציה.
האטה היא למעשה פונקציה שמקבלת ערך שבר בין 0 ל-1.0 ומחזירה מספר עשרוני. הערך שמוחזר יכול להיות מחוץ לגבולות כדי לייצג חריגה מעל או מתחת לטווח. אפשר ליצור פונקציית Easing בהתאמה אישית, כמו בקוד שבהמשך.
val CustomEasing = Easing { fraction -> fraction * fraction } @Composable fun EasingUsage() { val value by animateFloatAsState( targetValue = 1f, animationSpec = tween( durationMillis = 300, easing = CustomEasing ), label = "custom easing" ) // …… }
ב-Compose יש כמה פונקציות Easing מובנות שמתאימות לרוב תרחישי השימוש.
במאמר Speed - Material Design מוסבר איך לבחור את פונקציית ההאטה בהתאם לתרחיש.
FastOutSlowInEasingLinearOutSlowInEasingFastOutLinearEasingLinearEasingCubicBezierEasing- לצפייה במודעות נוספות
הנפשה של סוגי נתונים מותאמים אישית על ידי המרה אל AnimationVector וממנו
רוב ממשקי ה-API של האנימציה ב-Compose תומכים ב-Float, ב-Color, ב-Dp ובסוגי נתונים בסיסיים אחרים כערכי אנימציה כברירת מחדל, אבל לפעמים צריך להוסיף אנימציה לסוגי נתונים אחרים, כולל סוגים בהתאמה אישית. במהלך האנימציה, כל ערך של אנימציה מיוצג בתור AnimationVector. הערך מומר ל-AnimationVector ולהפך על ידי TwoWayConverter תואם, כדי שמערכת האנימציה המרכזית תוכל לטפל בהם באופן אחיד. לדוגמה, Int מיוצג כ-AnimationVector1D שמכיל ערך float יחיד.
TwoWayConverter של Int נראה כך:
val IntToVector: TwoWayConverter<Int, AnimationVector1D> = TwoWayConverter({ AnimationVector1D(it.toFloat()) }, { it.value.toInt() })
Color הוא למעשה קבוצה של 4 ערכים: אדום, ירוק, כחול ואלפא, ולכן Color מומר ל-AnimationVector4D שמכיל 4 ערכי float. כך, כל סוג נתונים שמשמש באנימציות מומר ל-AnimationVector1D, ל-AnimationVector2D, ל-AnimationVector3D או ל-AnimationVector4D, בהתאם לממדים שלו. כך אפשר להוסיף אנימציה לרכיבים שונים של האובייקט בנפרד, ולכל אחד מהם יש מעקב מהירות משלו. אפשר לגשת לממירים מובנים לסוגי נתונים בסיסיים באמצעות ממירים כמו Color.VectorConverter או Dp.VectorConverter.
כשרוצים להוסיף תמיכה בסוג נתונים חדש כערך אנימציה, אפשר ליצור TwoWayConverter משלכם ולספק אותו ל-API. לדוגמה, אתם יכולים להשתמש ב-animateValueAsState כדי להוסיף אנימציה לסוג הנתונים בהתאמה אישית, כך:
data class MySize(val width: Dp, val height: Dp) @Composable fun MyAnimation(targetSize: MySize) { val animSize: MySize by animateValueAsState( targetSize, TwoWayConverter( convertToVector = { size: MySize -> // Extract a float value from each of the `Dp` fields. AnimationVector2D(size.width.value, size.height.value) }, convertFromVector = { vector: AnimationVector2D -> MySize(vector.v1.dp, vector.v2.dp) } ), label = "size" ) }
הרשימה הבאה כוללת כמה VectorConverter מובנים:
Color.VectorConverterDp.VectorConverterOffset.VectorConverterInt.VectorConverterFloat.VectorConverterIntSize.VectorConverter
מומלץ בשבילכם
- הערה: טקסט הקישור מוצג כש-JavaScript מושבת
- אנימציות מבוססות-ערך
- פיתוח קוד איטרטיבי {:#iterative-code-dev }
- אנימציות ב-Compose