Gradien mesh membuat transisi warna multiarah yang kompleks menggunakan petak patch 2D. Tidak seperti gradien linear atau radial, gradien mesh menginterpolasi warna dengan lancar di seluruh petak 2D. Gunakan gradien mesh, bukan menumpuk
beberapa kuas radialGradient atau linearGradient saat membuat latar belakang yang mengalir,
organik, atau multi-titik di antarmuka pengguna Anda.
Konsep utama
Untuk membuat gradien mesh, tentukan dimensi petak, verteks, dan transisi warna di antara titik:
- Dimensi petak: Mesh dibagi menjadi beberapa patch di sepanjang sumbu vertikal dan
horizontal. Petak
rowsdancolumnsberisi (baris+1)×(kolom+1) verteks. Misalnya, mesh 1×1 terdiri dari 4 verteks yang membentuk satu patch. - Koordinat yang dinormalisasi: Semua posisi verteks menggunakan sistem koordinat yang dinormalisasi, dengan
(0f, 0f)mewakili kiri atas dan(1f, 1f)mewakili kanan bawah batas gambar. - Titik kontrol Bezier (garis singgung): Setiap verteks berisi hingga empat titik kontrol Bezier opsional. Garis singgung ini menentukan kelengkungan tepi
antara verteks yang berdekatan. Jika Anda menggunakan
Offset.Unspecified, Compose akan menyimpulkan tangen untuk memastikan transisi yang mulus di seluruh patch. Setiap sel petak yang dibentuk oleh 4 verteks beserta titik kontrolnya menghasilkan patch bezier. - Interpolasi warna: Framework menghitung warna di antara verteks utama. Tetapkan
hasBicubicColorketrueuntuk interpolasi Catmull-Rom guna mendapatkan pergeseran warna yang lebih halus, ataufalseuntuk interpolasi bilinear.
Menggambar dengan MeshGradientPainter
Di Jetpack Compose, gunakan
MeshGradientPainter
untuk merender gradien mesh. MeshGradientPainter menggambar di kanvas.
Membuat gradien mesh sederhana
Untuk membuat gradien mesh statis dasar, lakukan inisialisasi MeshGradientPainter dengan
menentukan dimensinya dan menggunakan fungsi setVertex di dalam
blok konfigurasi untuk memosisikan titik sudut dan menetapkan warnanya.
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) )
Menggunakan titik kontrol Bezier tertentu
Secara default, generator mesh menangani penghitungan yang kompleks untuk menjaga transisi petak tetap lancar. Namun, Anda dapat menyesuaikan tangen secara eksplisit pada satu verteks jika ingin mendorong, menarik, atau menjepit bagian warna tertentu secara selektif.
Offset kontrol diukur relatif terhadap posisi verteks 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) )
Membuat petak lanjutan
Contoh ini menunjukkan petak 3x3, yang berarti ada 16 titik yang perlu ditentukan, dengan titik tengah ditetapkan dengan offset yang berbeda:
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) // ... )
Menganimasikan gradien mesh
Karena parameter lambda block dari MeshGradientPainter dijalankan dalam
DrawScope, parameter ini dapat membaca dan mengamati status yang dapat diubah. Anda dapat menganimasikan posisi
atau warna dari waktu ke waktu tanpa mengalokasikan ulang shader atau 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) )