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 2D. Use gradientes de malha em vez de empilhar
vários pincéis radialGradient ou linearGradient ao criar planos de fundo fluidos,
orgânicos ou de vários pontos na interface do usuário.
Principais conceitos
Para criar 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 o canto superior esquerdo e(1f, 1f)representa o canto inferior direito dos limites do desenho. - Pontos de controle de Bezier (tangentes): cada vértice contém até quatro
pontos de controle de Bezier opcionais. Essas tangentes especificam a curvatura da aresta
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 de 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 interpolação bilinear.
Desenhar com o 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 Bezier específicos
Por padrão, o gerador de malha lida com cálculos complexos para manter as transições de grade suaves. No entanto, é possível personalizar explicitamente as tangentes em qualquer vértice se você quiser empurrar, puxar ou pinçar seletivamente determinadas seções de cor.
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 de 3 por 3, o que significa que há 16 pontos que precisam ser especificados, com os pontos do meio 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) )