Los gradientes de malla crean transiciones de color complejas y multidireccionales con una cuadrícula 2D de parches. A diferencia de los gradientes lineales o radiales, los gradientes de malla interpolan los colores de manera uniforme en una cuadrícula. Usa gradientes de malla para crear elementos estéticos fluidos y orgánicos en tu interfaz de usuario.
Conceptos clave
Para construir un gradiente de malla, define las dimensiones de la cuadrícula, los vértices y las transiciones de color entre los puntos:
- Dimensiones de la cuadrícula: La malla se divide en parches a lo largo de los ejes vertical y horizontal. Una cuadrícula de
rowsycolumnscontiene (rows+1) ×(columns+1) vértices. Por ejemplo, una malla de 1 × 1 consta de 4 vértices que forman un parche. - Coordenadas normalizadas: Todas las posiciones de los vértices usan un sistema de coordenadas normalizado en el que
(0f, 0f)representa la parte superior izquierda y(1f, 1f)representa la parte inferior derecha de los límites de dibujo. - Puntos de control de Bezier (tangentes): Cada vértice contiene hasta cuatro puntos de control de Bezier opcionales. Estas tangentes especifican la curvatura del borde entre los vértices vecinos. Si usas
Offset.Unspecified, Compose infiere las tangentes para garantizar transiciones uniformes en los parches. Cada celda de la cuadrícula formada por 4 vértices junto con sus puntos de control genera un parche de Bezier. - Interpolación de color: El framework calcula los colores entre los vértices principales. Establece
hasBicubicColorentruepara interpolación de Catmull-Rom para cambios de color más uniformes ofalsepara la interpolación bilineal.
Cómo dibujar con MeshGradientPainter
En Jetpack Compose, usa
MeshGradientPainter
para renderizar un gradiente de malla. MeshGradientPainter dibuja en el lienzo.
Cómo crear un gradiente de malla simple
Para crear un gradiente de malla estático básico, inicializa un MeshGradientPainter especificando sus dimensiones y usando la función setVertex dentro del bloque de configuración para posicionar los puntos de esquina y asignarles colores.
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) )
Cómo usar puntos de control de Bezier específicos
De forma predeterminada, el generador de malla controla cálculos complejos para mantener las transiciones de la cuadrícula uniformes. Sin embargo, puedes personalizar explícitamente las tangentes en cualquier vértice individual si deseas empujar, tirar o pellizcar de forma selectiva ciertas secciones de color.
Los desplazamientos de control se miden en relación con la posición del vértice 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) )
Cómo crear cuadrículas avanzadas
En este ejemplo, se muestra una cuadrícula de 3 por 3, lo que significa que se deben especificar 16 puntos, con los puntos medios establecidos con diferentes desplazamientos:
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) // ... )
Cómo animar un gradiente de malla
Debido a que el parámetro lambda block de MeshGradientPainter se ejecuta dentro de un DrawScope, puede leer y observar el estado mutable. Puedes animar posiciones o colores a lo largo del tiempo sin reasignar sombreadores ni mapas de bits.
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) )