メッシュ グラデーション

メッシュ グラデーションは、パッチの 2D グリッドを使用して、複雑な多方向の色遷移を作成します。線形グラデーションや円形グラデーションとは異なり、メッシュ グラデーションはグリッド全体で色をスムーズに補間します。メッシュ グラデーションを使用して、ユーザー インターフェースに流動的でオーガニックな美的要素を作成します。

現在のメッシュ グラデーション ポイントが表示されたメッシュ グラデーションの例。
図 1。現在のメッシュ グラデーション ポイントを表示したメッシュ グラデーションの例。

主なコンセプト

メッシュ グラデーションを作成するには、グリッドの寸法、頂点、ポイント間の色の遷移を定義します。

  • グリッドの寸法: メッシュは、垂直軸と水平軸に沿ってパッチに分割されます。rowscolumns のグリッドには、(rows+1)×(columns+1)個の頂点が含まれます。たとえば、1×1 のメッシュは 4 つの頂点で構成され、1 つのパッチを形成します。
  • 正規化された座標: すべての頂点の位置は、正規化された座標系を使用します。この座標系では、(0f, 0f) は描画範囲の左上、(1f, 1f) は描画範囲の右下を表します。
  • ベジェ曲線コントロール ポイント(接線): 各頂点には、最大 4 つのオプションのベジェ曲線コントロール ポイントが含まれます。これらの接線は、隣接する頂点間のエッジの曲率を指定します。Offset.Unspecified を使用すると、Compose はパッチ間のスムーズな遷移を確保するために接線を推測します。4 つの頂点とコントロール ポイントで形成される各グリッドセルは、ベジェ曲線パッチを生成します。
  • 色の補間: フレームワークは、メインの頂点間の色を計算します。色の変化をスムーズにする Catmull-Rom 補間 には hasBicubicColortrue に設定し、双線形補間には false に設定します。

MeshGradientPainter で描画する

Jetpack Compose でメッシュ グラデーションをレンダリングするには、 MeshGradientPainter を使用します。MeshGradientPainter はキャンバスに描画します。

シンプルなメッシュ グラデーションを作成する

基本的な静的メッシュ グラデーションを作成するには、寸法を指定して MeshGradientPainter を初期化し、構成ブロック内で setVertex 関数を使用してコーナー ポイントを配置して色を割り当てます。

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

各コーナーに 4 色が定義された基本的なメッシュ グラデーション
図 2。4 色の基本的なメッシュ グラデーション。各コーナーは 4 色のうちの 1 色に設定されています。

特定のベジェ曲線コントロール ポイントを使用する

デフォルトでは、メッシュ ジェネレータは複雑な計算を処理して、グリッドの遷移をスムーズに保ちます。ただし、特定の色セクションを選択的にプッシュ、プル、またはシャープにピンチする場合は、任意の単一の頂点で接線を明示的にカスタマイズできます。

コントロール オフセットは、ホスト頂点の位置を基準に測定されます。

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

左上の点が曲線になっているメッシュ グラデーション。
図 3。ベジェ曲線コントロール ポイントを使用して左上の頂点をカーブさせます。

高度なグリッドを作成する

この例では、3×3 のグリッドを示しています。つまり、16 個のポイントを指定する必要があります。中央のポイントは異なるオフセットで設定されています。

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

ベジエ コントロール ポイントと波の色を使用したメッシュ グラデーション。メッシュ ポイントがその上に描画されている。
図 4。ベジェ曲線コントロール ポイントとウェーブ カラーを使用したメッシュ グラデーション。メッシュ ポイントは上に示されています。

メッシュ グラデーションをアニメーション化する

MeshGradientPainterblock ラムダ パラメータは DrawScope 内で実行されるため、可変状態を読み取って監視できます。シェーダーやビットマップを再割り当てすることなく、位置や色を時間とともにアニメーション化できます。

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

図 5. アニメーションを示すポイントを含むアニメーション メッシュ グラデーション。