Os gradientes de malha criam transições de cores complexas e multidirecionais usando uma grade 2D de patches. Ao contrário dos gradientes lineares ou radiais, os gradientes de malha interpolam cores de maneira suave em uma grade. Use gradientes de malha para criar elementos estéticos fluidos e orgânicos na interface do usuário.
Principais conceitos
Para construir um gradiente de malha, defina as dimensões da grade, os vértices e as transições de cor entre os pontos:
- Dimensões da grade:a malha é dividida em patches ao longo dos eixos vertical e horizontal. Uma grade de
rowsecolumnscontém (linhas + 1) ×(colunas + 1) vértices. Por exemplo, uma malha 1 × 1 consiste em quatro vértices que formam um patch. - Coordenadas normalizadas:todas as posições de vértice usam um sistema de coordenadas normalizado em que
(0f, 0f)representa a parte de cima à esquerda e(1f, 1f)representa a parte de baixo à direita dos limites de desenho. - Pontos de controle de Bézier (tangentes) : cada vértice contém até quatro pontos de controle de Bézier opcionais. Essas tangentes especificam a curvatura da borda entre vértices vizinhos. Se você usar
Offset.Unspecified, o Compose vai inferir as tangentes para garantir transições suaves entre os patches. Cada célula da grade formada por quatro vértices com os pontos de controle gera um patch de Bézier. - Interpolação de cores:o framework calcula as cores entre os vértices principais. Defina
hasBicubicColorcomotruepara a interpolação Catmull-Rom para mudanças de cor mais suaves oufalsepara a interpolação bilinear.
Desenhar com MeshGradientPainter
No Jetpack Compose, use
MeshGradientPainter
para renderizar um gradiente de malha. MeshGradientPainter desenha na tela.
Criar um gradiente de malha simples
Para criar um gradiente de malha estático básico, inicialize um MeshGradientPainter especificando as dimensões dele e usando a função setVertex dentro do bloco de configuração para posicionar os pontos de canto e atribuir cores a eles.
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) )
Usar pontos de controle de Bézier específicos
Por padrão, o gerador de malha processa cálculos complexos para manter as transições de grade suaves. No entanto, é possível personalizar explicitamente as tangentes em qualquer vértice único se você quiser empurrar, puxar ou apertar seletivamente determinadas seções de cores.
Os deslocamentos de controle são medidos em relação à posição do vértice do 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) )
Criar grades avançadas
Este exemplo mostra uma grade 3 por 3, o que significa que há 16 pontos que precisam ser especificados, com os pontos intermediários definidos com deslocamentos diferentes:
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) // ... )
Animar um gradiente de malha
Como o parâmetro lambda block de MeshGradientPainter é executado em um DrawScope, ele pode ler e observar o estado mutável. É possível animar posições ou cores ao longo do tempo sem realocar sombreadores ou bitmaps.
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) )