Bevy UI 圆角迁移指南:BorderRadius 字段变为 CornerRadius,ResolvedBorderRadius 变为 Vec2
【免费下载链接】bevyA refreshingly simple>项目地址: https://gitcode.com/GitHub_Trending/be/bevy
本篇指南围绕 Bevy UI 的一次破坏性 API 变更展开:为支持椭圆(elliptical)圆角节点,BorderRadius的四个角字段由Val变为CornerRadius,ResolvedBorderRadius的字段由标量f32变为Vec2。读完本文,你将掌握新旧 API 的逐项对照写法、CornerRadius的循环/椭圆语义与解析(resolve)规则,并能顺畅地完成现有 UI 代码的迁移。
背景:为什么字段要变成二维
官方迁移指南 border_radius.md(对应 PR 24779)给出的变更说明只有一句话:
In order to support elliptical nodes, the fields of
BorderRadiusare nowCornerRadiuss and the fields ofResolvedBorderRadiusare nowVec2s.
即:为了支持椭圆圆角节点,BorderRadius的字段改为CornerRadius,ResolvedBorderRadius的字段改为Vec2。对应的功能发布说明见 Elliptical Border Radius。
在旧版 API 中,每个角的圆角半径只有一个Val,隐含"圆形"语义。新版允许 x(水平)与 y(垂直)半径不同,从而画出一个椭圆的角。所有改动都集中在bevy_ui的两个文件里:
- crates/bevy_ui/src/geometry.rs:新增的
CornerRadius类型; - crates/bevy_ui/src/ui_node.rs:
BorderRadius与ResolvedBorderRadius。
迁移对照:BorderRadius 的新旧写法
变更前
BorderRadius { pub top_left: px(10.), pub top_right: percent(20.), pub bottom_right: zero(), pub bottom_left: vh(5.), }变更后
BorderRadius { pub top_left: CornerRadius::circular(px(10.)), pub top_right: CornerRadius::circular(percent(20.)), pub bottom_right: CornerRadius::circular(zero()), pub bottom_left: CornerRadius::circular(vh(5.)), }由于CornerRadius实现了From<Val>(见 geometry.rs),你也可以用into完成同样的转换:
BorderRadius { pub top_left: px(10.).into(), pub top_right: percent(20.).into(), pub bottom_right: zero().into(), pub bottom_left: vh(5.).into(), }从源码结构看,From<Val>的实现是Self { x, y: auto() },即把原值放进x、y设为Val::Auto——这正好命中下一节讲的"圆形"表示。因此绝大多数只写圆形圆角的旧代码可以靠.into()最小改动地迁移。
新类型 CornerRadius:结构与圆形/椭圆语义
CornerRadius的定义在 crates/bevy_ui/src/geometry.rs:
pub struct CornerRadius { /// Responsive horizontal radius. pub x: Val, /// Responsive vertical radius. pub y: Val, }两个字段都是响应式的Val,可以取px、percent、vh/vw、em/rem等任意响应式值。
圆形圆角的表示方式:Val::Auto
迁移指南明确说明:圆形圆角(circular corner radius)的表示方式是让CornerRadius::x或CornerRadius::y其中一个为Val::Auto。
对应的构造辅助函数是circular(geometry.rs):
/// Creates a circular corner radius, with `radius` resolved relative to the node's /// shortest side and clamped to half its length. pub const fn circular(radius: Val) -> Self { Self { x: radius, y: Val::Auto, } }CornerRadius还提供了两组常量和两个常用构造器:
| 常量/方法 | 定义 | 语义 |
|---|---|---|
CornerRadius::MAX | { x: Px(f32::MAX), y: Val::Auto } | 完全圆角:半径为节点最短边的一半,节点呈胶囊形(宽高相等时为圆形) |
CornerRadius::MAX_ELLIPTICAL | { x: Px(f32::MAX), y: Px(f32::MAX) } | 完全椭圆角:水平半径为宽的一半,垂直半径为高的一半,节点被画成椭圆 |
CornerRadius::ZERO | { x: ZERO, y: ZERO } | 直角 |
circular(radius) | { x: radius, y: Auto } | 圆形角,半径相对节点最短边解析,并钳制到其一半 |
all(radius) | { x: radius, y: radius } | 两轴同值。注意:由于各轴独立解析(percent 等值对宽高各自取值),解析结果不一定相等 |
new(x, y) | { x, y } | 椭圆角,分别指定水平/垂直半径 |
一个容易踩的点是all与circular的区别。源码文档测试(geometry.rs)表明:对 100x50 的节点,all(px(30.))解析为Vec2::new(30., 25.)——x 轴钳制到宽度一半(50),y 轴钳制到高度一半(25);而circular(px(30.))会先对最短边(50)解析,再钳制到 25,得到Vec2::splat(25.),保证两轴严格相等、形状是圆弧而不是椭弧。
可传入的输入类型:From 实现一览
BorderRadius的所有构造函数参数类型是impl Into<CornerRadius>,因此凡是可以转换为CornerRadius的类型都能传入。源码中提供了如下转换(geometry.rs):
From<Val>:px(10.).into(),等价于circular;From<(Val, Val)>:(px(10.), px(20.)).into();From<[Val; 2]>:[px(10.), px(20.)].into()。
此外BorderRadius本身实现了From<T: Into<CornerRadius>>(ui_node.rs),直接调用Self::all(value),所以BorderRadius::from(px(10.))等于四角全圆角。
一个值得注意的设计细节是PartialEq的实现(geometry.rs):{ x: v, y: Auto } == { x: Auto, y: v }判定为相等。因为Auto只是"另一轴未显式设置、按圆形半径解释"的标记,两种摆放方式语义相同。这意味着CornerRadius::circular(r)与{ x: r, y: auto() }、{ x: auto(), y: r }在比较时一致,迁移后做assert_eq!时不必纠结Auto放在哪一轴。
resolve:解析成物理像素的规则
CornerRadius::resolve(geometry.rs)把响应式半径解析为物理像素Vec2,签名与Val::resolve一致:
pub fn resolve( self, scale_factor: f32, size: Vec2, // 节点尺寸 viewport_size: Vec2, em_size: EmSize, rem_size: RemSize, ) -> Vec2分支逻辑是:
- 两轴都是
Auto→ 返回Vec2::ZERO; - 一轴为
Auto(即圆形模式)→ 用非 Auto 轴的值对节点最短边size.min_element()解析,钳制到[0, 0.5 * min(size)],然后splat到两轴; - 其余情况(椭圆模式)→ x 对
size.x(宽度)解析、y 对size.y(高度)解析,各自钳制到[0, 0.5 * size]。
同文件的单元测试corner_radius_resolve(geometry.rs)覆盖了这些行为:100x50 节点上{x: Px(100.), y: Auto}解析为vec2(25., 25.)(圆形钳制),{x: Px(40.), y: Px(40.)}解析为vec2(40., 25.)(椭圆钳制)。这也印证了BorderRadius文档中"半径若超过节点宽/高的一半,会被计算为高/宽的一半"的说明(ui_node.rs)。
BorderRadius 构造器与更新函数不再 const
迁移指南指出:BorderRadius的构造函数和更新函数不再是const,以便参数可以接受任意实现Into<CornerRadius>的类型:
let n = BorderRadius::top_right(vh(10.)); let m = BorderRadius::top_right([px(10.), px(20.)]);实现上,all、new、top_left/top_right/bottom_right/bottom_left、left/right/top/bottom以及with_*系列方法全部改为普通函数,参数为impl Into<CornerRadius>(ui_node.rs)。例如:
pub fn all(radius: impl Into<CornerRadius>) -> Self { let radius = radius.into(); Self { top_left: radius, top_right: radius, bottom_left: radius, bottom_right: radius, } }两个例外值得注意:px(f32, f32, f32, f32)与percent(f32, f32, f32, f32)这四个纯浮点参数的便捷构造器仍然是const fn(ui_node.rs),因为它们内部直接写CornerRadius::circular(Val::Px(...)),不经过Into转换。
BorderRadius的常量同步扩展:DEFAULT/ZERO(直角)、MAX(胶囊/圆形)、新增的MAX_ELLIPTICAL(椭圆)(ui_node.rs)。
典型写法速查
结合源码文档示例(ui_node.rs),四角混合圆角、圆角与椭圆角并存的完整写法是:
fn setup_ui(mut commands: Commands) { commands.spawn(( Node { width: Val::Px(100.), height: Val::Px(100.), border: UiRect::all(Val::Px(2.)), border_radius: BorderRadius { // 圆角,x 和 y 半径相等 top_left: CornerRadius::circular(px(10.)), // From<Val> 简写 top_right: percent(20.).into(), // 椭圆角 bottom_right: CornerRadius::new(px(30.), px(20.)), // 结构体字面量 bottom_left: CornerRadius { x: px(10.), y: px(40.) }, }, ..Default::default() }, BackgroundColor(BLUE.into()), )); }ResolvedBorderRadius:解析结果从标量到 Vec2
ResolvedBorderRadius是渲染侧消费的解析结果。变更后(ui_node.rs):
/// The values are in physical pixels. pub struct ResolvedBorderRadius { pub top_left: Vec2, pub top_right: Vec2, pub bottom_right: Vec2, pub bottom_left: Vec2, }- 每个角是一个
Vec2,单位为物理像素,x/y 分别对应水平/垂直半径; BorderRadius::resolve只是对四角逐一调用CornerRadius::resolve(ui_node.rs),签名包含scale_factor、node_size、viewport_size、em_size、rem_size;- 该类型实现了
From<ResolvedBorderRadius> for [[f32; 4]; 2](ui_node.rs),把四个角摊平成"第一行 4 个 x 半径、第二行 4 个 y 半径"的二维数组,从源码结构看这正是上传给 UI 渲染着色器的布局。bevy_ui_render中的节点矩形、文字、阴影等渲染模块都消费该类型。
如果你之前直接读取ResolvedBorderRadius.top_left等字段做算术(它曾是标量),需要改为访问.x/.y分量。
被移除的 API:resolve_single_corner
迁移指南最后一项:
BorderRadius::resolve_single_cornerhas been removed, useCornerRadius::resolveinstead.
即单角解析入口下移到了CornerRadius上,参数为(scale_factor, size, viewport_size, em_size, rem_size),返回该角的物理像素Vec2。需要单角数值(例如自定义裁剪矩形)时,直接对你感兴趣的那个CornerRadius字段调用resolve即可。
迁移后的真实用法验证
仓库中的示例与插件代码可以作为迁移后的参照:
- UI 边框示例 examples/ui/styling/borders.rs 新增了四组椭圆圆角用例,同时展示了三种写法:结构体字面量
CornerRadius { x: px(25), y: px(8) }、元组转换(px(8), px(25)).into()、数组转换[px(8), px(25)].into(),以及percent半径的椭圆角; - 控件库
bevy_feathers的分段按钮圆角工具 rounded_corners.rs 中,RoundedCorners::to_border_radius通过CornerRadius::from(px(radius))构造圆角,再组合成BorderRadius——这是"旧代码用From迁移"的典型形态。
迁移检查清单
- 把
BorderRadius四个角字段中的Val值包一层CornerRadius::circular(...)或.into()(语义等价,推荐.into()保持代码简洁); - 构造器调用如
BorderRadius::top_right(vh(10.))无需改动即可接受Val、(Val, Val)、[Val; 2]、CornerRadius任意类型,需要椭圆角时传入两值即可; - 若依赖了
const上下文里调用all/with_*等,需改为运行期调用;纯px/percent常量构造仍可在const中使用; - 读取
ResolvedBorderRadius的地方改为Vec2分量运算; - 删除对
BorderRadius::resolve_single_corner的调用,换成对应角上的CornerRadius::resolve。
完成以上步骤后,你的 UI 代码即兼容新版 Bevy 的椭圆圆角能力,并且可以随时用CornerRadius::new(px(30.), px(20.))这类写法获得 CSSborder-radius风格的多值椭圆角效果。
【免费下载链接】bevyA refreshingly simple>项目地址: https://gitcode.com/GitHub_Trending/be/bevy
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考