מעברי צבעים ברשת יוצרים מעברי צבעים מורכבים ורב-כיווניים באמצעות רשת דו-ממדית של טלאים. בניגוד למעברי צבע לינאריים או רדיאליים, מעברי צבע ברשת מבצעים אינטרפולציה חלקה של צבעים ברשת דו-ממדית. כשיוצרים רקעים זורמים, אורגניים או עם כמה נקודות בממשק המשתמש, כדאי להשתמש במעברי צבע ברשת במקום לערום כמה מברשות radialGradient או linearGradient.
מושגים מרכזיים
כדי ליצור מעבר צבעים ברשת, מגדירים את מידות הרשת, את הקודקודים ואת מעברי הצבעים בין הנקודות:
- מידות הרשת: הרשת מחולקת לחלקים לאורך הצירים האנכיים והאופקיים. רשת של
rowsו-columnsמכילה (שורות+1)×(עמודות+1) קודקודים. לדוגמה, רשת בגודל 1×1 מורכבת מ-4 קודקודים שיוצרים טלאי אחד. - קואורדינטות מנורמלות: כל המיקומים של הקודקודים משתמשים במערכת קואורדינטות מנורמלת, שבה
(0f, 0f)מייצג את הפינה השמאלית העליונה ו-(1f, 1f)מייצג את הפינה הימנית התחתונה של גבולות השרטוט. - נקודות בקרה של עקומת בזייה (משיקים): כל קודקוד מכיל עד ארבע נקודות בקרה אופציונליות של עקומת בזייה. המשיקים האלה מציינים את העקמומיות של הקצה בין קודקודים שכנים. אם משתמשים ב-
Offset.Unspecified, Compose מסיק את המשיקים כדי להבטיח מעברים חלקים בין הטלאים. כל תא ברשת שנוצר על ידי 4 קודקודים יחד עם נקודות הבקרה שלהם יוצר טלאי בזייה. - אינטרפולציית צבעים: המסגרת מחשבת את הצבעים בין הקודקודים הראשיים. מגדירים את
hasBicubicColorלערךtrueכדי להשתמש באינטרפולציה של Catmull-Rom למעברי צבע חלקים יותר, או לערךfalseכדי להשתמש באינטרפולציה בילינארית.
ציור באמצעות MeshGradientPainter
ב-Jetpack Compose, משתמשים ב-MeshGradientPainter כדי ליצור שיפוע רשת. MeshGradientPainter מצייר על הקנבס.
יצירת מעבר צבעים פשוט ברשת
כדי ליצור מעבר צבעים סטטי בסיסי, מאתחלים MeshGradientPainter על ידי ציון המימדים שלו ושימוש בפונקציה setVertex בתוך בלוק ההגדרות כדי למקם את נקודות הפינה ולהקצות להן צבעים.
val rows = 1 val columns = 1 val gradientPainter = remember { MeshGradientPainter(rows, columns) { // Parameters: row, column, position, color setVertex(0, 0, Offset(0f, 0f), Color.Red) // Top-Left setVertex(0, 1, Offset(1f, 0f), Color.Blue) // Top-Right setVertex(1, 0, Offset(0f, 1f), Color.Green) // Bottom-Left setVertex(1, 1, Offset(1f, 1f), Color.Yellow) // Bottom-Right } } Box( modifier = modifier .aspectRatio(16/9f) .fillMaxWidth() .paint(gradientPainter) )
שימוש בנקודות בקרה ספציפיות של Bezier
כברירת מחדל, מחולל הרשת מטפל בחישובים מורכבים כדי לשמור על מעברים חלקים ברשת. עם זאת, אתם יכולים להתאים אישית באופן מפורש את המשיקים בכל קודקוד בודד אם אתם רוצים לדחוף, למשוך או לצבוט בחדות קטעי צבע מסוימים באופן סלקטיבי.
ההיסטים של אמצעי הבקרה נמדדים ביחס למיקום של קודקוד המארח.
val customTangentPainter = remember { MeshGradientPainter(rows = 1, columns = 1) { // Tweak the top-left vertex to curve outwards to the right and bottom setVertex( row = 0, column = 0, position = Offset(0f, 0f), color = Color.Magenta, rightControlPoint = Offset(0.4f, 0.1f), bottomControlPoint = Offset(0.1f, 0.4f) ) // Other points can remain unspecified to use default inferred fallback tangents setVertex(0, 1, Offset(1f, 0f), Color.Cyan) setVertex(1, 0, Offset(0f, 1f), Color.Blue) setVertex(1, 1, Offset(1f, 1f), Color.Black) } } Box( modifier = modifier .aspectRatio(16/9f) .fillMaxWidth() .paint(customTangentPainter) )
יצירת רשתות מתקדמות
בדוגמה הזו מוצגת רשת של 3 על 3, כלומר יש 16 נקודות שצריך לציין, כשהנקודות האמצעיות מוגדרות עם היסטים שונים:
val points = remember { listOf( Offset(0.0f, 0.0f), Offset(0.3f, 0.0f), Offset(0.7f, 0.0f), Offset(1.0f, 0.0f), Offset(0.0f, 0.3f), Offset(0.2f, 0.4f), Offset(0.7f, 0.2f), Offset(1.0f, 0.3f), Offset(0.0f, 0.7f), Offset(0.3f, 0.8f), Offset(0.7f, 0.6f), Offset(1.0f, 0.7f), Offset(0.0f, 1.0f), Offset(0.3f, 1.0f), Offset(0.7f, 1.0f), Offset(1.0f, 1.0f) ) } val gradientPainter = remember { MeshGradientPainter(rows = 3, columns = 3) { // Row 0 setVertex(0, 0, points[0], yellow) setVertex(0, 1, points[1], orange) setVertex(0, 2, points[2], yellow) setVertex(0, 3, points[3], purple) // Row 1 setVertex(1, 0, points[4], pink) setVertex(1, 1, points[5], yellow) setVertex(1, 2, points[6], pink) setVertex(1, 3, points[7], purple) // Row 2 setVertex(2, 0, points[8], indigo) setVertex(2, 1, points[9], pink) setVertex(2, 2, points[10], purple) setVertex(2, 3, points[11], indigo) // Row 3 setVertex(3, 0, points[12], purple) setVertex(3, 1, points[13], indigo) setVertex(3, 2, points[14], pink) setVertex(3, 3, points[15], yellow) } } Box( modifier = modifier.padding(32.dp) .aspectRatio(16 / 9f) .fillMaxWidth() .paint(gradientPainter) // ... )
הנפשה של מעבר צבעים ברשת
מכיוון שהפרמטר lambda block של MeshGradientPainter מופעל בתוך DrawScope, הוא יכול לקרוא מצב שניתן לשינוי ולצפות בו. אפשר להוסיף אנימציה למיקומים או לצבעים לאורך זמן בלי להקצות מחדש הצללות או מפות סיביות.
val infiniteTransition = rememberInfiniteTransition(label = "meshMovement") val animatedOffset by infiniteTransition.animateFloat( initialValue = -0.1f, targetValue = 0.1f, animationSpec = infiniteRepeatable( animation = tween(2500, easing = LinearEasing), repeatMode = RepeatMode.Reverse ), label = "offset" ) val coral = Color(255, 90, 90) val peach = Color(255, 139, 90) val amber = Color(255, 169, 90) val sunshine = Color(255, 212, 90) val indigo = Color(0xFF5856D6) val pink = Color(0xFFFF2D55) val gradientPainter = remember { MeshGradientPainter(rows = 3, columns = 3) { // Row 0 setVertex(0, 0, Offset(0.0f, 0.0f), indigo) setVertex(0, 1, Offset(0.3f, 0.0f), peach) setVertex(0, 2, Offset(0.7f, 0.0f), amber) setVertex(0, 3, Offset(1.0f, 0.0f), sunshine) // Row 1 setVertex(1, 0, Offset(0.0f, 0.3f), pink) setVertex(1, 1, Offset(0.2f, 0.4f) + Offset(animatedOffset, animatedOffset), coral) setVertex(1, 2, Offset(0.7f, 0.2f) + Offset(animatedOffset, animatedOffset), peach) setVertex(1, 3, Offset(1.0f, 0.3f), indigo) // Row 2 setVertex(2, 0, Offset(0.0f, 0.7f), coral) setVertex(2, 1, Offset(0.3f, 0.8f) + Offset(animatedOffset, 0f), pink) setVertex(2, 2, Offset(0.7f, 0.6f) + Offset(animatedOffset, 0f), sunshine) setVertex(2, 3, Offset(1.0f, 0.7f), amber) // Row 3 setVertex(3, 0, Offset(0.0f, 1.0f), sunshine) setVertex(3, 1, Offset(0.3f, 1.0f), amber) setVertex(3, 2, Offset(0.7f, 1.0f), pink) setVertex(3, 3, Offset(1.0f, 1.0f), indigo) } } Box( modifier = modifier.padding(32.dp) .safeContentPadding() .aspectRatio(16 / 9f) .fillMaxWidth() .paint(gradientPainter) )