Gradienty siatkowe

Gradienty siatki tworzą złożone, wielokierunkowe przejścia kolorów za pomocą dwuwymiarowej siatki łat. W przeciwieństwie do gradientów liniowych i promieniowych gradienty siatki płynnie interpolują kolory w siatce 2D. Podczas tworzenia płynnych, organicznych lub wielopunktowych tła w interfejsie użytkownika używaj gradientów siatki zamiast nakładania na siebie wielu pędzli radialGradient lub linearGradient.

Przykład gradientu siatki z wyświetlonymi bieżącymi punktami gradientu siatki.
Rysunek 1. Przykład gradientu siatki z wyświetlonymi bieżącymi punktami gradientu siatki.

Kluczowe pojęcia

Aby utworzyć gradient siatki, określ wymiary siatki, wierzchołki i przejścia kolorów między punktami:

  • Wymiary siatki: siatka jest podzielona na fragmenty wzdłuż osi pionowej i poziomej. Siatka o wymiarach rows i columns zawiera (wiersze + 1) ×(kolumny + 1) wierzchołków. Na przykład siatka 1 × 1 składa się z 4 wierzchołków tworzących 1 płat.
  • Znormalizowane współrzędne: wszystkie pozycje wierzchołków używają znormalizowanego układu współrzędnych, w którym (0f, 0f) reprezentuje lewy górny róg, a (1f, 1f) – prawy dolny róg granic rysunku.
  • Punkty kontrolne krzywej Beziera (styczne): każdy wierzchołek zawiera maksymalnie 4 opcjonalne punkty kontrolne krzywej Beziera. Te styczne określają krzywiznę krawędzi między sąsiednimi wierzchołkami. Jeśli używasz Offset.Unspecified, Compose wnioskuje o stycznych, aby zapewnić płynne przejścia między łatkami. Każda komórka siatki utworzona przez 4 wierzchołki wraz z ich punktami kontrolnymi generuje łatę Beziera.
  • Interpolacja kolorów: platforma oblicza kolory między głównymi wierzchołkami. Ustaw hasBicubicColor na true, aby zastosować interpolację Catmulla-Roma, która zapewnia płynniejsze przejścia kolorów, lub na false, aby zastosować interpolację dwuliniową.

Rysuj za pomocą MeshGradientPainter

W Jetpack Compose użyj MeshGradientPainter, aby wyrenderować gradient siatki. MeshGradientPainter rysuje na obszarze roboczym.

Tworzenie prostego gradientu siatki

Aby utworzyć podstawowy statyczny gradient siatki, zainicjuj MeshGradientPainter, określając jego wymiary i używając funkcji setVertex w bloku konfiguracji, aby ustawić punkty narożne i przypisać im kolory.

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)
)

Podstawowy gradient siatki z 4 kolorami zdefiniowanymi w każdym rogu
Rysunek 2. Podstawowy gradient siatki z 4 kolorami, z których każdy jest ustawiony w jednym z 4 rogów.

Użyj określonych punktów kontrolnych krzywej Beziera

Domyślnie generator siatki wykonuje złożone obliczenia, aby zapewnić płynne przejścia siatki. Możesz jednak dostosować styczne na dowolnym wierzchołku, jeśli chcesz selektywnie przesuwać, przeciągać lub ostro ściskać określone sekcje kolorów.

Przesunięcia elementów sterujących są mierzone względem pozycji wierzchołka hosta.

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)
)

Gradient siatki z zakrzywionym punktem w lewym górnym rogu.
Rysunek 3. Zaokrąglij lewy górny wierzchołek za pomocą punktu kontrolnego krzywej Beziera.

Tworzenie zaawansowanych siatek

Ten przykład pokazuje siatkę 3 x 3, co oznacza, że należy określić 16 punktów, przy czym punkty środkowe mają różne przesunięcia:

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)
        // ...
)

Gradient siatki z punktami kontrolnymi krzywej Beziera i kolorami fal oraz punktami siatki nałożonymi na gradient.
Rysunek 4. Gradient siatki z punktami kontrolnymi krzywej Beziera i kolorami fal oraz punktami siatki nałożonymi na niego.

Animowanie gradientu siatki

Parametr lambda block funkcji MeshGradientPainter jest wykonywany w ramach DrawScope, więc może odczytywać i obserwować stan zmienny. Możesz animować pozycje lub kolory w czasie bez ponownego przydzielania cieniowania lub 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)
)

Rysunek 5. Animowany gradient siatki z punktami pokazującymi animację.