Gradienty siatki tworzą złożone, wielokierunkowe przejścia kolorów za pomocą dwuwymiarowej siatki łat. W przeciwieństwie do gradientów liniowych i promieniowych gradienty siatki płynnie interpolują kolory w siatce 2D. Podczas tworzenia płynnych, organicznych lub wielopunktowych tła w interfejsie użytkownika używaj gradientów siatki zamiast nakładania na siebie wielu pędzli radialGradient lub linearGradient.
Kluczowe pojęcia
Aby utworzyć gradient siatki, określ wymiary siatki, wierzchołki i przejścia kolorów między punktami:
- Wymiary siatki: siatka jest podzielona na fragmenty wzdłuż osi pionowej i poziomej. Siatka o wymiarach
rowsicolumnszawiera (wiersze + 1) ×(kolumny + 1) wierzchołków. Na przykład siatka 1 × 1 składa się z 4 wierzchołków tworzących 1 płat. - Znormalizowane współrzędne: wszystkie pozycje wierzchołków używają znormalizowanego układu współrzędnych, w którym
(0f, 0f)reprezentuje lewy górny róg, a(1f, 1f)– prawy dolny róg granic rysunku. - Punkty kontrolne krzywej Beziera (styczne): każdy wierzchołek zawiera maksymalnie 4 opcjonalne punkty kontrolne krzywej Beziera. Te styczne określają krzywiznę krawędzi między sąsiednimi wierzchołkami. Jeśli używasz
Offset.Unspecified, Compose wnioskuje o stycznych, aby zapewnić płynne przejścia między łatkami. Każda komórka siatki utworzona przez 4 wierzchołki wraz z ich punktami kontrolnymi generuje łatę Beziera. - Interpolacja kolorów: platforma oblicza kolory między głównymi wierzchołkami. Ustaw
hasBicubicColornatrue, aby zastosować interpolację Catmulla-Roma, która zapewnia płynniejsze przejścia kolorów, lub nafalse, aby zastosować interpolację dwuliniową.
Rysuj za pomocą MeshGradientPainter
W Jetpack Compose użyj MeshGradientPainter, aby wyrenderować gradient siatki. MeshGradientPainter rysuje na obszarze roboczym.
Tworzenie prostego gradientu siatki
Aby utworzyć podstawowy statyczny gradient siatki, zainicjuj MeshGradientPainter, określając jego wymiary i używając funkcji setVertex w bloku konfiguracji, aby ustawić punkty narożne i przypisać im kolory.
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) )
Użyj określonych punktów kontrolnych krzywej Beziera
Domyślnie generator siatki wykonuje złożone obliczenia, aby zapewnić płynne przejścia siatki. Możesz jednak dostosować styczne na dowolnym wierzchołku, jeśli chcesz selektywnie przesuwać, przeciągać lub ostro ściskać określone sekcje kolorów.
Przesunięcia elementów sterujących są mierzone względem pozycji wierzchołka hosta.
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) )
Tworzenie zaawansowanych siatek
Ten przykład pokazuje siatkę 3 x 3, co oznacza, że należy określić 16 punktów, przy czym punkty środkowe mają różne przesunięcia:
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) // ... )
Animowanie gradientu siatki
Parametr lambda block funkcji MeshGradientPainter jest wykonywany w ramach DrawScope, więc może odczytywać i obserwować stan zmienny. Możesz animować pozycje lub kolory w czasie bez ponownego przydzielania cieniowania lub bitmap.
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) )