מילוי הדרגתי של רשת יוצר מעברי צבע מורכבים ורב-כיווניים באמצעות רשת דו-ממדית של טלאים. בניגוד לשינויי צבע הדרגתיים לינאריים או רדיאליים, שינויי צבע הדרגתיים ברשת מבצעים אינטרפולציה חלקה של צבעים ברשת. אפשר להשתמש במעברי צבע רציפים כדי ליצור אלמנטים אסתטיים זורמים ואורגניים בממשק המשתמש.
מושגים מרכזיים
כדי ליצור מעבר צבעים ברשת, מגדירים את הממדים של הרשת, את הקודקודים ואת מעברי הצבעים בין הנקודות:
- מידות הרשת: הרשת מחולקת לטלאים לאורך הציר האנכי והציר האופקי. רשת של
rowsו-columnsמכילה (rows+1)×(columns+1) קודקודים. לדוגמה, רשת 1x1 מורכבת מ-4 קודקודים שיוצרים טלאי אחד. - קואורדינטות מנורמלות: כל מיקומי הקודקודים משתמשים במערכת קואורדינטות מנורמלת שבה
(0f, 0f)מייצג את הפינה השמאלית העליונה ו-(1f, 1f)מייצג את הפינה הימנית התחתונה של גבולות הציור. - נקודות בקרה של בזייה (משיקים): כל קודקוד מכיל עד ארבע נקודות בקרה אופציונליות של בזייה. המשיקים האלה מציינים את עקמומיות הקצה בין קודקודים סמוכים. אם משתמשים ב-
Offset.Unspecified, התכונה 'יצירה' מסיקה את הקווים המשיקים כדי להבטיח מעברים חלקים בין הטלאים. כל תא ברשת שנוצר על ידי 4 קודקודים יחד עם נקודות הבקרה שלהם יוצר טלאי בזייה. - שילוב צבעים: המסגרת מחשבת את הצבעים בין קודקודי הליבה. מגדירים את
hasBicubicColorל-trueבשביל Catmull-Rom interpolation כדי לקבל מעברי צבע חלקים יותר, או ל-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) )
שימוש בנקודות בקרה ספציפיות של בזייה
כברירת מחדל, מחולל הרשת מבצע חישובים מורכבים כדי לשמור על מעברים חלקים בין המשבצות. עם זאת, אתם יכולים להתאים אישית באופן מפורש את המשיקים בכל קודקוד בודד אם אתם רוצים לדחוף, למשוך או לצבוט בחדות חלקים מסוימים של צבע.
ההיסטים של נקודות הבקרה נמדדים ביחס למיקום של קודקוד המארח.
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) )
יצירת רשתות מתקדמות
בדוגמה הזו מוצגת רשת בגודל 3x3, כלומר יש 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) )