Gradien mesh membuat transisi warna yang kompleks dan multi-arah menggunakan petak 2D. Tidak seperti gradien linear atau radial, gradien mesh menginterpolasi warna dengan lancar di seluruh petak. Gunakan gradien mesh untuk membuat elemen estetika yang lancar dan organik di antarmuka pengguna.
Konsep utama
Untuk membuat gradien mesh, tentukan dimensi petak, verteks, dan transisi warna antar-titik:
- Dimensi petak: Mesh dibagi menjadi petak di sepanjang sumbu vertikal dan horizontal. Petak
rowsdancolumnsberisi (rows+1)×(columns+1) verteks. Misalnya, mesh 1×1 terdiri dari 4 verteks yang membentuk satu petak. - Koordinat yang dinormalisasi: Semua posisi verteks menggunakan sistem koordinat yang dinormalisasi dengan
(0f, 0f)yang mewakili kiri atas dan(1f, 1f)yang 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 garis singgung untuk memastikan transisi yang lancar di seluruh petak. Setiap sel petak yang dibentuk oleh 4 verteks beserta titik kontrolnya akan menghasilkan petak bezier. - Interpolasi warna: Framework menghitung warna 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, inisialisasi MeshGradientPainter dengan menentukan dimensinya dan menggunakan fungsi setVertex di dalam blok konfigurasi untuk memposisikan 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 perhitungan kompleks untuk menjaga transisi petak tetap lancar. Namun, Anda dapat menyesuaikan garis singgung secara eksplisit pada satu verteks jika ingin mendorong, menarik, atau mencubit 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 3 x 3, yang berarti ada 16 titik yang perlu ditentukan, dengan titik tengah yang 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 dieksekusi 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) )