网格渐变

网格渐变使用由多个色块组成的二维网格创建复杂的多向颜色过渡效果。与线性渐变或径向渐变不同,网格渐变可在网格中平滑地插值颜色。使用网格渐变在界面中创建流畅自然的审美元素。

网格渐变示例,显示了其当前的网格渐变点。
图 1. 网格渐变示例,其中显示了当前的网格渐变点。

主要概念

如需构建网格渐变,请定义网格维度、顶点以及点之间的颜色过渡:

  • 网格维度:网格沿垂直轴和水平轴拆分为多个块。rowscolumns 的网格包含 (rows+1)×(columns+1) 个顶点。例如,1x1 网格由 4 个顶点组成,形成一个面片。
  • 归一化坐标:所有顶点位置都使用归一化坐标系,其中 (0f, 0f) 表示绘制边界的左上角,(1f, 1f) 表示绘制边界的右下角。
  • Bezier 控制点(切线):每个顶点最多包含四个可选的 Bezier 控制点。这些正切指定了相邻顶点之间的边曲率。如果您使用 Offset.Unspecified,Compose 会推断出切线,以确保补丁之间的平滑过渡。由 4 个顶点及其控制点形成的每个网格单元都会生成一个贝塞尔曲面片。
  • 颜色插值:框架会计算主要顶点之间的颜色。将 hasBicubicColor 设置为 true 可实现 Catmull-Rom 插值,从而实现更平滑的颜色过渡;设置为 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)
)

基本网格渐变,每个角定义了 4 种颜色
图 2. 一个包含四种颜色的基本网格渐变,每个角都设置为四种颜色之一。

使用特定的贝塞尔控制点

默认情况下,网格生成器会处理复杂的计算,以保持网格过渡平滑。不过,如果您想有选择地推拉或急剧收缩某些颜色部分,可以明确自定义任何单个顶点的切线。

控制偏移量是相对于宿主顶点的位置来衡量的。

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)