SuperAPI AI 工具集

smooth-scrim-gradient

前端与建站

查看项目

更新于 10-17 16:17

技能介绍

用途

scrim 是位于图片与下方页面之间的一段渐变,使图像看起来溶解进背景。若按最直观的写法 Brush.verticalGradient(listOf(Color.Transparent, background)) 实现,会同时产生两个瑕疵:淡出开始处有一条可见的线,以及中段出现一条脏带。两者都可修复,且都不是靠增加 alpha 能解决的。

主要能力

该技能提供两个主要函数(代码保持原样):

// adapted (the colour lerp is imported unqualified here)
fun smoothScrimBrush(
    from: Color,
    to: Color,
    startFraction: Float = 0f,
    endFraction: Float = 1f,
    startY: Float = 0f,
    endY: Float = Float.POSITIVE_INFINITY,
    steps: Int = 24,
): Brush =
    Brush.verticalGradient(
        colorStops =
            Array(steps + 1) { i ->
                val t = i / steps.toFloat()
                val position = startFraction + (endFraction - startFraction) * t
                position to lerp(from, to, t * t * (3f - 2f * t))
            },
        startY = startY,
        endY = endY,
    )

// the common case: ramp one colour from invisible to opaque
fun artworkScrimBrush(color: Color, steps: Int = 24): Brush =
    smoothScrimBrush(from = color.copy(alpha = 0f), to = color, steps = steps)

关键陷阱与做法

  • 线性斜坡有拐角,而拐角就是肉眼看到的边缘。人眼对亮度变化率的敏感度高于亮度本身,因此直线斜坡离开 0 和到达 1 处的不连续会被读成一条画出来的线。smoothstep,t²(3−2t),在 t=0 和 t=1 处斜率为零,两端都没有拐角。两端都要缓动:只缓动起点,只会把接缝移到 scrim 底部,而不是消除它。
  • Color.Transparent 是 alpha 为 0 的黑色,因此带颜色的淡出会拖过黑色。应当把透明端改为 yourColor.copy(alpha = 0f),这样 RGB 通道不会移动。
  • 自己插值颜色并交出多个色标。两个色标等于把整条曲线交给渲染器:你得到它的插值方式、颜色空间和预乘选择,而这些在不同后端间不同。预先插值色标固定了色标值本身,渲染器只处理它们之间的短线段;这也是把非线性曲线放进没有缓动参数的 brush 的唯一方法。
  • 两个色标无法表达曲线。即使手上有 smoothstep,两端各一个色标的渐变还是一条直线;曲线只存在于你发出的色标中。足够多的色标能把 S 曲线变成分段线性近似,其折角低于可见阈值。
  • 步数要通过观察确定,而不是照抄数字。色标太少,线段连接处会显示为淡带;太多则是着色器中的浪费。在你交付的最深背景上渲染 scrim(深色背景是 8 位量化影响最大的地方),将数量减半直到出现条带,然后退回一档。示例中的 steps = 24 是在该测试中存活下来的值,是起点而不是常量。
  • 用分位而不是缩小盒子来限制斜坡。短的 scrim 就是陡的 scrim,而陡正是让淡出被读成边缘的原因。startFraction/endFraction(或已知像素时的 startY/endY)保持盒子高度,同时限制斜坡发生的位置。
  • tile mode 只在三条路径中的一条上起作用。默认 endY = Float.POSITIVE_INFINITY 路径下,着色器用盒子自身高度替代无限坐标,斜坡正好覆盖盒子,渐变参数不会离开 [0,1],tile mode 不会被查询。startFraction/endFraction 路径通过把色标位置向内移动来限制斜坡,平坦端由首末色标保持,也不是 tile mode。只有盒子内部显式的 startY/endY 组合会留下斜坡之外的区域交给 tile mode 负责;改成 repeat 或 mirror 只影响这一条路径。
  • 淡出到 scrim 后面真正存在的颜色。scrim 底部必须与页面背景完全相等,否则只是把接缝挪了位置。当背景由图片推导而来时,两者必须来自同一来源(参见兄弟技能 artwork-palette-theming,以及无图片项的回退 deterministic-title-placeholder-painter)。

使用方式与验证

在仓库根目录运行以下只读命令进行验证:

检查渐变调用内没有透明色标。任何命中要么是注释,要么是 bug,逐一阅读:

grep -rn -A4 --include='*.kt' -E "verticalGradient\(|horizontalGradient\(|linearGradient\(" . \
  | grep -v '/build/' | grep "Color.Transparent"

列出代码库中所有渐变 brush,以便检查每一个要么已缓动、要么是有意线性(小瓦片上的双色装饰渐变不需要曲线;scrim 需要):

grep -rln --include='*.kt' -E "verticalGradient\(|horizontalGradient\(" . | grep -v '/build/'

视觉验证:把 scrim 放在平坦的中间色调填充上而不是图片上。图片会掩盖条带和接缝,平坦填充会立刻暴露两者。然后在最深背景上重复。

适用场景与限制

适用于 Kotlin/Jetpack Compose 中图片与页面背景之间的渐变遮罩。资料未提供收费、价格、平台支持范围或效果保证等信息。资料未提供该技能是否适用于其他 UI 框架或编程语言的说明。资料未提供官方文档链接或版本信息。