การไล่ระดับสีแบบเมชจะสร้างการเปลี่ยนสีแบบหลายทิศทางที่ซับซ้อนโดยใช้ตารางกริด 2 มิติของแพตช์ การไล่ระดับสีแบบเมชจะประมาณค่าสีในตารางกริด 2 มิติได้อย่างราบรื่น ซึ่งแตกต่างจากการไล่ระดับสีแบบเส้นตรงหรือแบบรัศมี ใช้การไล่ระดับสีแบบเมชแทนการซ้อนแปรง radialGradient หรือ linearGradient หลายๆ อันเมื่อสร้างพื้นหลังแบบไหล แบบออร์แกนิก หรือแบบหลายจุดในอินเทอร์เฟซผู้ใช้
แนวคิดสำคัญ
หากต้องการสร้างการไล่ระดับสีแบบเมช ให้กำหนดขนาดตารางกริด จุดยอด และการเปลี่ยนสีระหว่างจุดต่างๆ ดังนี้
- ขนาดตารางกริด: เมชจะแบ่งออกเป็นแพตช์ตามแกนแนวตั้งและแนวนอน ตารางกริดขนาด
rowsและcolumnsมีจุดยอด (แถว+1)×(คอลัมน์+1) จุด ตัวอย่างเช่น เมช 1×1 ประกอบด้วยจุดยอด 4 จุดที่รวมกันเป็นแพตช์ 1 รายการ - พิกัดที่ปรับมาตรฐาน: ตำแหน่งจุดยอดทั้งหมดใช้ระบบพิกัดที่ปรับมาตรฐาน โดยที่
(0f, 0f)แสดงถึงมุมซ้ายบน และ(1f, 1f)แสดงถึงมุมขวาล่างของขอบเขตการวาด - จุดควบคุม Bezier (แทนเจนต์): จุดยอดแต่ละจุดมีจุดควบคุม Bezier ที่ไม่บังคับได้สูงสุด 4 จุด เส้นสัมผัสเหล่านี้จะระบุความโค้งของขอบระหว่างจุดยอดที่อยู่ติดกัน หากคุณใช้
Offset.Unspecified, Compose จะอนุมานแทนเจนต์เพื่อให้แน่ใจว่าการเปลี่ยนผ่านระหว่างแพตช์ต่างๆ เป็นไปอย่างราบรื่น เซลล์ตารางกริดแต่ละเซลล์ที่เกิดจากจุดยอด 4 จุดพร้อมกับจุดควบคุมจะสร้างแพตช์เบซิเยร์ - การประมาณค่าสี: เฟรมเวิร์กจะคำนวณสีระหว่างจุดยอดหลัก ตั้งค่า
hasBicubicColorเป็นtrueสำหรับการประมาณค่าในช่วงแบบ Catmull-Rom เพื่อให้การเปลี่ยนสีราบรื่นขึ้น หรือfalseสำหรับการประมาณค่าในช่วงแบบ Bilinear
วาดด้วย MeshGradientPainter
ใน Jetpack Compose ให้ใช้
MeshGradientPainter
เพื่อแสดงผลการไล่ระดับสีแบบเมช MeshGradientPainter วาดบน Canvas
สร้างการไล่ระดับสีแบบเมชอย่างง่าย
หากต้องการสร้างการไล่ระดับสีแบบเมชคงที่พื้นฐาน ให้เริ่มต้น 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) )
ใช้จุดควบคุม Bezier ที่เฉพาะเจาะจง
โดยค่าเริ่มต้น เครื่องมือสร้าง Mesh จะจัดการการคำนวณที่ซับซ้อนเพื่อให้การเปลี่ยนตารางกริดเป็นไปอย่างราบรื่น อย่างไรก็ตาม คุณสามารถปรับแต่งเส้นสัมผัสบนจุดยอดจุดเดียวได้อย่างชัดเจนหากต้องการเลือกดัน ดึง หรือบีบส่วนสีบางส่วนอย่างชัดเจน
ระบบจะวัดออฟเซ็ตการควบคุมโดยสัมพันธ์กับตำแหน่งของจุดยอดโฮสต์
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) // ... )
สร้างภาพเคลื่อนไหวให้กับการไล่ระดับสีแบบเมช
เนื่องจากพารามิเตอร์ Lambda block ของ MeshGradientPainter ทำงานภายใน 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) )