Độ chuyển màu dạng lưới tạo ra các độ chuyển màu phức tạp, đa hướng bằng cách sử dụng lưới 2D gồm các mảng. Không giống như độ chuyển màu tuyến tính hoặc xuyên tâm, độ chuyển màu dạng lưới sẽ nội suy màu một cách mượt mà trên lưới. Hãy sử dụng độ chuyển màu dạng lưới để tạo các phần tử thẩm mỹ hữu cơ và linh hoạt trong giao diện người dùng.
Khái niệm chính
Để tạo độ chuyển màu dạng lưới, hãy xác định kích thước lưới, các đỉnh và độ chuyển màu giữa các điểm:
- Kích thước lưới: Lưới được chia thành các mảng dọc theo trục dọc và trục ngang. Lưới gồm
rowsvàcolumnschứa (hàng + 1) ×(cột + 1) đỉnh. Ví dụ: lưới 1×1 gồm 4 đỉnh tạo thành một mảng. - Toạ độ được chuẩn hoá: Tất cả vị trí đỉnh đều sử dụng hệ toạ độ được chuẩn hoá, trong đó
(0f, 0f)biểu thị vị trí trên cùng bên trái và(1f, 1f)biểu thị vị trí dưới cùng bên phải của ranh giới bản vẽ. - Điểm kiểm soát Bezier (tiếp tuyến): Mỗi đỉnh chứa tối đa 4 điểm kiểm soát Bezier không bắt buộc. Các tiếp tuyến này chỉ định độ cong của cạnh giữa các đỉnh lân cận. Nếu bạn sử dụng
Offset.Unspecified, Compose sẽ suy luận các tiếp tuyến để đảm bảo độ chuyển mượt mà trên các mảng. Mỗi ô lưới được tạo thành từ 4 đỉnh cùng với các điểm kiểm soát sẽ tạo ra một mảng Bezier. - Nội suy màu: Khung này tính toán màu giữa các đỉnh chính. Đặt
hasBicubicColorthànhtrueđể nội suy Catmull-Rom nhằm chuyển màu mượt mà hơn hoặcfalseđể nội suy song tuyến.
Vẽ bằng MeshGradientPainter
Trong Jetpack Compose, hãy sử dụng
MeshGradientPainter
để kết xuất độ chuyển màu dạng lưới. MeshGradientPainter vẽ trên canvas.
Tạo độ chuyển màu dạng lưới đơn giản
Để tạo độ chuyển màu dạng lưới tĩnh cơ bản, hãy khởi chạy MeshGradientPainter bằng cách chỉ định kích thước và sử dụng hàm setVertex bên trong khối cấu hình để đặt các điểm góc và gán màu cho các điểm đó.
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) )
Sử dụng các điểm kiểm soát Bezier cụ thể
Theo mặc định, trình tạo lưới sẽ xử lý các phép tính phức tạp để giữ cho độ chuyển lưới mượt mà. Tuy nhiên, bạn có thể tuỳ chỉnh rõ ràng các tiếp tuyến trên bất kỳ đỉnh nào nếu muốn chọn đẩy, kéo hoặc véo mạnh một số phần màu nhất định.
Các độ lệch kiểm soát được đo tương ứng với vị trí của đỉnh chính.
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) )
Tạo lưới nâng cao
Ví dụ này cho thấy lưới 3 x 3, tức là có 16 điểm cần chỉ định, với các điểm ở giữa được đặt với các độ lệch khác nhau:
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) // ... )
Tạo ảnh động cho độ chuyển màu dạng lưới
Vì tham số lambda block của MeshGradientPainter được thực thi trong DrawScope, nên tham số này có thể đọc và quan sát trạng thái có thể thay đổi. Bạn có thể tạo ảnh động cho vị trí hoặc màu theo thời gian mà không cần phân bổ lại chương trình đổ bóng hoặc ảnh 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) )