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 1x1 consiste em quatro vértices formando um patch. - Coordenadas normalizadas:todas as posições de vértice usam um sistema de coordenadas normalizadas em que
(0f, 0f)representa a parte superior esquerda e(1f, 1f)representa a parte inferior direita dos limites do 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ê usa
Offset.Unspecified, o Compose infere as tangentes para garantir transições suaves entre os patches. Cada célula da grade formada por quatro vértices e seus 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 interpolação de Catmull-Rom para transições de cores mais suaves oufalsepara 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ática 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 da grade suaves. No entanto, é possível personalizar explicitamente as tangentes em qualquer vértice único se quiser empurrar, puxar ou pinçar 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, ou seja, há 16 pontos que precisam ser especificados, com os pontos intermediários definidos com diferentes deslocamentos:
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 shaders 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) )