מילוי הדרגתי של רשת יוצר מעברי צבע מורכבים ורב-כיווניים באמצעות רשת דו-ממדית של טלאים. בניגוד לשינויי צבע הדרגתיים לינאריים או רדיאליים, שינויי צבע הדרגתיים ברשת מבצעים אינטרפולציה חלקה של צבעים ברשת. אפשר להשתמש במעברי צבע רציפים כדי ליצור אלמנטים אסתטיים זורמים ואורגניים בממשק המשתמש.
מושגים מרכזיים
כדי ליצור מעבר צבעים ברשת, מגדירים את הממדים של הרשת, את הקודקודים ואת מעברי הצבעים בין הנקודות:
- מידות הרשת: הרשת מחולקת לטלאים לאורך הצירים האנכיים והאופקיים. רשת בגודל
rowsו-columnsמכילה (rows+1)×(columns+1) קודקודים. לדוגמה, רשת בגודל 1×1 מורכבת מ-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) // ... )
יצירת אנימציה של הדרגת רשת
מכיוון שהפרמטר block lambda של 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) )