用途
该技能解决 Compose 中「按任意角度绘制线性渐变」的问题。Brush.linearGradient 接收的是两个端点坐标而非角度,因此「30° 的渐变」实际要回答的是:两个端点应放在哪里,才能让渐变恰好以 30° 横跨整个盒子。答案取决于盒子的宽高比,所以同一个 brush 无法在组合(composition)阶段一次性构建,必须放在绘制修饰符内,此时尺寸已知。
主要能力
提供 Modifier.angledGradientBackground(colors: List<Color>, degrees: Float) 扩展。其逻辑为:
- 取盒子尺寸
(x, y),计算gamma = atan2(y, x),即盒子自身对角线的角度,它是「射线从垂直边离开」与「从水平边离开」的分界。 - 将角度归一化到
[0, 360),再转为弧度alpha。 - 按
alpha所在区间计算gradientLength:落在垂直边区间时为x / cos(alpha),水平边区间时为y / sin(alpha),其余象限用对应的负值形式,兜底为hypot(x, y)。 - 由
gradientLength得到offsetX、offsetY,以盒子中心为基准构造Brush.linearGradient的 start/end,并用drawRect绘制。
公式要点:γ = atan2(h, w) 是中心射线穿过盒子角点的角度。垂直边情形下长度为 w / |cos α|,因此水平半偏移恰为 w/2,端点按构造落在边上;另一坐标 (w/2)·tan α 由区间判断保证不超过 h/2。
使用方式
将 angledGradientBackground 作为 Modifier 链的一环使用,传入颜色列表与角度(度)。资料强调:不要在其下方再设置 background(...) 颜色,因为 drawBehind 每次绘制都会重绘,第二层不透明背景属于不可见的无用功。
适用场景
当设计需要非轴对齐角度的渐变,或该角度需要动画时,应使用此角度计算方案。轴对齐方向(左→右、上→下)无需这套计算,可直接用无限端点坐标让 Brush.linearGradient 代入盒子尺寸。
限制与陷阱
- 旋转对角线会超出边界,截断到边缘会扭曲角度:用长度
hypot(w, h)/2的向量旋转,仅四个角点正确;其他位置中心到边界的距离更小,端点会落到盒子外,只显示渐变中段,永远到不了两端颜色。把超出的点裁剪回最近边缘虽能修正超出,却会横向移动该点,使两端连线不再是指定角度。 - 比较前必须归一化角度:Kotlin 的
%保留被除数符号,-30f % 360仍为-30f,会穿过所有区间判断落入兜底分支。余数为负时需加 360。 - 退化盒子产生零长度渐变而非除零:
γ会塌缩为 0(零高)或 π/2(零宽),此时所选分支的分子正是塌缩的维度,长度算出来为 0,两端点都落在中心。真正的0/0需要除数也恰好为零,而由Float的 π 构造出的这些角度不会出现该情况。仍应对γ提前退出:此时什么都画不出来,且盒子在获得尺寸前确实会被测量为零。 - 保留不可达的
else:四个区间在数学上覆盖[0, 2π],但用浮点比较且边界为闭区间,γ是Float而由 π 推导的边界是Double。去掉else会导致编译失败(作为表达式的when必须穷尽),而对角线是兜底的合理答案。 - 屏幕角度为顺时针,因为 y 向下增长:0° 从左到右,45° 从左上到右下,90° 从上到下。从逆时针测量的设计工具移植角度时需取负,而上述归一化陷阱正是使其安全的前提。
- 应拦截
colors.size < 2:单色是调用方错误(只有一端的「渐变」),拦截成本仅一次比较。 - 轴对齐方向无需这套计算:
Brush.linearGradient会用盒子尺寸替换无限端点坐标。在ui-graphics-android1.12.0-alpha03 中,其createShader会对四个坐标与Float.POSITIVE_INFINITY比较并替换为size.width/size.height。注意Offset.Infinite在两个坐标上都是无限,端点会落在远角,渐变沿atan2(h, w)即盒子对角线方向,只有正方形时才是 45°。
验证方法
- 查找所有调用点并核对角度常量是否符合上述顺时针约定:
grep -rn --include='*.kt' "angledGradientBackground(" . | grep -v '/build/' - 查找应改用该修饰符的手写端点计算(start/end 为计算值而非
Offset.Zero/Offset.Infinite):grep -rn -A6 --include='*.kt' "Brush.linearGradient(" . | grep -v '/build/' | grep -E "start =|end =" - 用两个差异最大的颜色(纯红到纯蓝、无 alpha)验证两端点确实落在边缘:盒子角落应能看到两种纯色;端点超出的渐变只会显示浑浊的紫色。
- 在渐变显示时把盒子从正方形改为很宽再改回,可见角度必须不变——这正是逐象限分支的意义,也是固定端点渐变会出错的地方。
其他说明
资料中给出的代码为「改编版(已裁剪守卫子句)」,并提示被移除的守卫见 Traps 部分。分类方面,该技能属于 Compose UI 绘制与图形处理,资料未提供更多平台、收费或价格信息。