Le sfumature mesh creano transizioni di colore complesse e multidirezionali utilizzando una griglia 2D di patch. A differenza delle sfumature lineari o radiali, le sfumature a griglia interpolano uniformemente i colori in una griglia. Utilizza le sfumature mesh per creare elementi estetici fluidi e organici nell'interfaccia utente.
Concetti fondamentali
Per creare una sfumatura a rete, definisci le dimensioni della griglia, i vertici e le transizioni di colore tra i punti:
- Dimensioni griglia:la mesh viene suddivisa in patch lungo gli assi verticale e
orizzontale. Una griglia di
rowsecolumnscontiene (righe+1)×(colonne+1) vertici. Ad esempio, una mesh 1×1 è costituita da 4 vertici che formano una patch. - Coordinate normalizzate: tutte le posizioni dei vertici utilizzano un sistema di coordinate normalizzate in cui
(0f, 0f)rappresenta l'angolo in alto a sinistra e(1f, 1f)rappresenta l'angolo in basso a destra dei limiti del disegno. - Punti di controllo di Bézier (tangenti): ogni vertice contiene fino a quattro punti di controllo di Bézier facoltativi. Queste tangenti specificano la curvatura del bordo tra i vertici adiacenti. Se utilizzi
Offset.Unspecified, Compose deduce le tangenti per garantire transizioni fluide tra le patch. Ogni cella della griglia formata da quattro vertici insieme ai relativi punti di controllo genera una patch di Bézier. - Interpolazione del colore:il framework calcola i colori tra i vertici principali. Imposta
hasBicubicColorsutrueper l'interpolazione Catmull-Rom per transizioni di colore più fluide ofalseper l'interpolazione bilineare.
Disegna con MeshGradientPainter
In Jetpack Compose, utilizza
MeshGradientPainter
per eseguire il rendering di una sfumatura a rete. MeshGradientPainter disegna sul canvas.
Creare una sfumatura a mesh semplice
Per creare un gradiente a mesh statico di base, inizializza un MeshGradientPainter specificando le relative dimensioni e utilizzando la funzione setVertex all'interno del blocco di configurazione per posizionare i punti angolari e assegnare loro i colori.
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) )
Utilizzare punti di controllo di Bézier specifici
Per impostazione predefinita, il generatore di mesh gestisce calcoli complessi per mantenere fluide le transizioni della griglia. Tuttavia, puoi personalizzare esplicitamente le tangenti su qualsiasi singolo vertice se vuoi spingere, tirare o pizzicare in modo selettivo determinate sezioni di colore.
Gli offset di controllo vengono misurati rispetto alla posizione del vertice host.
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) )
Creare griglie avanzate
Questo esempio mostra una griglia 3x3, il che significa che devono essere specificati 16 punti, con i punti centrali impostati con offset diversi:
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) // ... )
Animare una sfumatura a rete
Poiché il parametro lambda block di MeshGradientPainter viene eseguito all'interno di
un DrawScope, può leggere e osservare lo stato modificabile. Puoi animare le posizioni
o i colori nel tempo senza riassegnare shader o 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) )