I gradienti mesh creano transizioni di colore complesse e multidirezionali utilizzando una griglia 2D di patch. A differenza delle sfumature lineari o radiali, le sfumature mesh interpolano i colori in modo fluido su una griglia 2D. Utilizza i gradienti mesh anziché impilare più pennelli radialGradient o linearGradient quando crei sfondi fluidi, organici o multipunto nella tua interfaccia utente.
Concetti fondamentali
Per creare un gradiente mesh, definisci le dimensioni della griglia, i vertici e le transizioni di colore tra i punti:
- Dimensioni della griglia: la mesh è suddivisa in patch lungo gli assi verticale e orizzontale. Una griglia di
rowsecolumnscontiene (righe+1)×(colonne+1) vertici. Ad esempio, una mesh 1x1 è costituita da 4 vertici che formano una patch. - Coordinate normalizzate: tutte le posizioni dei vertici utilizzano un sistema di coordinate normalizzato 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 4 vertici insieme ai relativi punti di controllo genera una patch di Bézier. - Interpolazione dei colori:il framework calcola i colori tra i vertici principali. Imposta
hasBicubicColorsutrueper l'interpolazione Catmull-Rom per variazioni di colore più fluide o sufalseper l'interpolazione bilineare.
Disegna con MeshGradientPainter
In Jetpack Compose, utilizza
MeshGradientPainter
per eseguire il rendering di un gradiente mesh. MeshGradientPainter disegna sul canvas.
Crea una sfumatura a trama semplice
Per creare un gradiente di mesh statico di base, inizializza un MeshGradientPainter specificandone le dimensioni e utilizza la funzione setVertex all'interno del blocco di configurazione per posizionare i punti d'angolo 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) )
Utilizza 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 dei controlli vengono misurati in base 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) )
Crea 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) // ... )
Anima un gradiente mesh
Poiché il parametro lambda block di MeshGradientPainter viene eseguito all'interno di un DrawScope, può leggere e osservare lo stato modificabile. Puoi animare posizioni o colori nel tempo senza riallocare 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) )