メッシュ グラデーションは、パッチの 2D グリッドを使用して、複雑な多方向の色遷移を作成します。線形グラデーションや円形グラデーションとは異なり、メッシュ グラデーションはグリッド全体で色をスムーズに補間します。メッシュ グラデーションを使用して、ユーザー インターフェースに流動的でオーガニックな美的要素を作成します。
主なコンセプト
メッシュ グラデーションを作成するには、グリッドの寸法、頂点、ポイント間の色の遷移を定義します。
- グリッドの寸法: メッシュは、垂直軸と水平軸に沿ってパッチに分割されます。
rowsとcolumnsのグリッドには、(rows+1)×(columns+1)個の頂点が含まれます。たとえば、1×1 のメッシュは 4 つの頂点で構成され、1 つのパッチを形成します。 - 正規化された座標: すべての頂点の位置は、正規化された座標系を使用します。この座標系では、
(0f, 0f)は描画範囲の左上、(1f, 1f)は描画範囲の右下を表します。 - ベジェ曲線コントロール ポイント(接線): 各頂点には、最大 4 つのオプションのベジェ曲線コントロール ポイントが含まれます。これらの接線は、隣接する頂点間のエッジの曲率を指定します。
Offset.Unspecifiedを使用すると、Compose はパッチ間のスムーズな遷移を確保するために接線を推測します。4 つの頂点とコントロール ポイントで形成される各グリッドセルは、ベジェ曲線パッチを生成します。 - 色の補間: フレームワークは、メインの頂点間の色を計算します。色の変化をスムーズにする Catmull-Rom
補間 には
hasBicubicColorをtrueに設定し、双線形補間には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) )
特定のベジェ曲線コントロール ポイントを使用する
デフォルトでは、メッシュ ジェネレータは複雑な計算を処理して、グリッドの遷移をスムーズに保ちます。ただし、特定の色セクションを選択的にプッシュ、プル、またはシャープにピンチする場合は、任意の単一の頂点で接線を明示的にカスタマイズできます。
コントロール オフセットは、ホスト頂点の位置を基準に測定されます。
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 のグリッドを示しています。つまり、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) // ... )
メッシュ グラデーションをアニメーション化する
MeshGradientPainter の block ラムダ パラメータは 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) )