Bei Mesh-Farbverläufen werden komplexe, multidirektionale Farbverläufe mithilfe eines 2D-Rasters aus Patches erstellt. Im Gegensatz zu linearen oder radialen Farbverläufen werden bei Mesh-Farbverläufen Farben gleichmäßig über ein Raster hinweg interpoliert. Mit Mesh-Farbverläufen können Sie fließende und organische ästhetische Elemente in Ihrer Benutzeroberfläche erstellen.
Wichtige Konzepte
Um einen Mesh-Farbverlauf zu erstellen, definieren Sie die Rasterabmessungen, die Eckpunkte und die Farbverläufe zwischen den Punkten:
- Rasterabmessungen:Das Mesh wird entlang der vertikalen und horizontalen Achse in Patches unterteilt. Ein Raster aus
rowsundcolumnsenthält (rows+1)×(columns+1) Eckpunkte. Ein 1×1-Mesh besteht beispielsweise aus vier Eckpunkten, die ein Patch bilden. - Normalisierte Koordinaten:Alle Eckpunktpositionen verwenden ein normalisiertes Koordinatensystem, wobei
(0f, 0f)die obere linke und(1f, 1f)die untere rechte Ecke der Zeichenbegrenzungen darstellt. - Bezier-Kontrollpunkte (Tangenten) : Jeder Eckpunkt enthält bis zu vier optionale Bezier-Kontrollpunkte. Diese Tangenten geben die Kantenkrümmung zwischen benachbarten Eckpunkten an. Wenn Sie
Offset.Unspecifiedverwenden, leitet Compose die Tangenten ab, um für reibungslose Übergänge zwischen den Patches zu sorgen. Jede Rasterzelle, die aus vier Eckpunkten und ihren Kontrollpunkten besteht, erzeugt ein Bezier-Patch. - Farbinterpolation:Das Framework berechnet die Farben zwischen den Haupteckpunkten. Legen Sie
hasBicubicColorauftruefür die Catmull-Rom Interpolation für sanftere Farbverläufe oderfalsefür die bilineare Interpolation fest.
Mit MeshGradientPainter zeichnen
Verwenden Sie in Jetpack Compose
MeshGradientPainter, um einen Mesh-Farbverlauf zu rendern. MeshGradientPainter zeichnet auf dem Canvas.
Einfachen Mesh-Farbverlauf erstellen
Um einen einfachen statischen Mesh-Farbverlauf zu erstellen, initialisieren Sie einen MeshGradientPainter, indem Sie seine Abmessungen angeben und die Funktion setVertex im Konfigurationsblock verwenden, um die Eckpunkte zu positionieren und ihnen Farben zuzuweisen.
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) )
Bestimmte Bezier-Kontrollpunkte verwenden
Standardmäßig übernimmt der Mesh-Generator komplexe Berechnungen, um die Übergänge im Raster reibungslos zu gestalten. Sie können jedoch Tangenten für jeden einzelnen Eckpunkt explizit anpassen, wenn Sie bestimmte Farbabschnitte selektiv verschieben, ziehen oder scharf zusammenziehen möchten.
Kontroll-Offsets werden relativ zur Position des Host-Eckpunkts gemessen.
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) )
Erweiterte Raster erstellen
In diesem Beispiel wird ein 3×3-Raster gezeigt. Das bedeutet, dass 16 Punkte angegeben werden müssen, wobei die Mittelpunkte mit unterschiedlichen Offsets festgelegt sind:
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) // ... )
Mesh-Farbverlauf animieren
Da der Lambda-Parameter block von MeshGradientPainter in einem DrawScope ausgeführt wird, kann er den veränderlichen Status lesen und beobachten. Sie können Positionen oder Farben im Zeitverlauf animieren, ohne Shader oder Bitmaps neu zuzuweisen.
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) )