Bevy UI 椭圆圆角(Elliptical Border Radius)完全指南:CornerRadius 与每轴独立半径的实战解析 Bevy UI 椭圆圆角Elliptical Border Radius完全指南CornerRadius 与每轴独立半径的实战解析【免费下载链接】bevyA refreshingly simple>项目地址: https://gitcode.com/GitHub_Trending/be/bevy本文深入讲解 Bevy UI 新引入的椭圆圆角elliptical border radius能力BorderRadius各角字段升级为CornerRadius后开发者可以为节点每个圆角的水平x与垂直y两个轴分别指定曲率半径从而绘制出正圆角、椭圆角乃至胶囊形、纯椭圆形的 UI 节点。读完本文你将掌握CornerRadius的取值语义、Val::Auto圆形约定、解析与钳制规则以及 API 迁移路径并能直接在真实 UI 布局中写出可运行的圆角代码。从“圆形圆角”到“椭圆圆角”一次 API 模型的跃迁在 Bevy 的早期版本中BorderRadius的四个字段top_left/top_right/bottom_right/bottom_left各自只承载一个Val值也就是说每个角只能表达“单一半径的圆弧”——水平和垂直方向的曲率天然绑定无法画出椭圆形的角。引入椭圆角支持后Bevy UI 可以在节点的几何边缘上绘制真正的椭圆弧见 _release-content/release-notes/elliptical_border_radius.md圆角circular cornerx、y 两个轴半径一致是最常见的“胶囊/圆角矩形”效果椭圆角elliptical cornerx、y 两个轴半径不同可产生类似 CSSborder-radius: 30px 20px的非对称倒角纯椭圆 / 胶囊节点当四个角的半径取到节点尺寸的极限值时节点整体会被绘制为胶囊形宽高相等则为圆形。该能力对应的数据模型变更集中在BorderRadius的字段类型上它的四个字段现在都变成了CornerRadius而CornerRadius本身是一对Valx / y。渲染侧为每个角分别使用水平、垂直两个方向上的半径绘制几何见 ui.wesl 中的sd_rounded_box。// BorderRadius 的新字段类型ui_node.rs pub struct BorderRadius { pub top_left: CornerRadius, pub top_right: CornerRadius, pub bottom_right: CornerRadius, pub bottom_left: CornerRadius, }认识 CornerRadiusx / y 两个轴的半径CornerRadius定义在bevy_ui的 geometry 模块中结构非常简单——只有两个Val字段但语义需要仔细理解pub struct CornerRadius { /// 响应式水平半径Responsive horizontal radius。 pub x: Val, /// 响应式垂直半径Responsive vertical radius。 pub y: Val, }官方文档geometry.rs 顶部注释给出的核心语义如下x、y 分别表示圆角椭圆弧在水平轴与垂直轴上的半径。若其中一轴为Val::Auto则解析后的半径将是圆形并钳制到节点最短边的一半若任一轴为 0或两轴都为Auto则该角为直角。为便于使用CornerRadius提供了一组构造常量与方法全部为const fn成员语义CornerRadius::ZEROx、y均为 0直角CornerRadius::MAX圆角取到极限x: Px(f32::MAX)、y: Auto等价于“节点最短边一半”的完全圆角节点变成胶囊/圆形CornerRadius::MAX_ELLIPTICALx、y均为极大值节点被画成“水平半径为半宽、垂直半径为半高的椭圆”CornerRadius::circular(radius)创建圆形角x radius、y Auto见源码 geometry.rs#L1334-L1339CornerRadius::all(radius)两轴使用同一个radius但两轴会独立解析、结果未必相等CornerRadius::new(x, y)直接分别指定水平、垂直半径同时实现了三条From转换使传入单个值/元组/数组都非常顺手geometry.rs#L1440-L1456impl FromVal for CornerRadius // 等价于 CornerRadius { x, y: auto() }即“圆形” impl From(Val, Val) for CornerRadius impl From[Val; 2] for CornerRadiusVal::Auto的约定一种轴代表“取对侧值”Auto在CornerRadius中承担了特殊的“未设置/继承对侧”职责x为Auto、y有值 → 结果等价于y有值、x有值的圆形半径y为Auto、x有值 → 同上解析后取节点最短边作为基准再钳制到一半两轴都为Auto即Default→ 直角Vec2::ZERO。这一点在解析代码里体现得非常直接geometry.rs#L1396-L1428只要有一轴是Auto就走size.min_element()节点短边分支并.clamp(0., 0.5 * size.min_element())两轴都有具体值时则各轴独立 resolve 后整体.clamp(Vec2::ZERO, 0.5 * size)。更有趣的是CornerRadius对PartialEq的定制geometry.rs#L1458-L1468CornerRadius { x: v, y: Auto }与CornerRadius { x: Auto, y: v }被视为相等——这正是下文“旧代码无需改动”的基石。BorderRadius 构造器全面解析不再 const改收 IntoBorderRadius有一批保持 const的便捷构造器用于快速生成“等半径圆形角”// ui_node.rs pub const ZERO: Self; // 全直角 pub const MAX: Self; // 全 MAX胶囊/圆形 pub const MAX_ELLIPTICAL: Self; // 全 MAX_ELLIPTICAL纯椭圆 pub const fn px(tl: f32, tr: f32, br: f32, bl: f32) - Self; // 逻辑像素圆角 pub const fn percent(tl: f32, tr: f32, br: f32, bl: f32) - Self; // 百分比圆角而单角/边设置类构造器update functions则取消了const参数类型由Val改为impl IntoCornerRadius从而既能传单个Val自动变成圆角也能传CornerRadius、(Val, Val)或[Val; 2]。这是本次变更中最需要留意的一处破坏性改动见迁移指南 _release-content/migration-guides/border_radius.md。它们包括all、new、top_left、top_right、bottom_right、bottom_left、left、right、top、bottom以及返回 builder 的with_top_left/with_top_right/with_right/with_top等一整套ui_node.rs#L3003-L3207。例use bevy::ui::prelude::*; // all四个角统一 let a BorderRadius::all(CornerRadius::circular(vh(10.))); let b BorderRadius::all(vh(10.)); // a b单个 Val 自动圆角化 // 只设右上角为“x10px、y20px”的椭圆角其余角保持直角 let c BorderRadius::top_right(CornerRadius::new(px(10.), px(20.))); // 传入数组表达每轴半径 let d BorderRadius::top_right([px(10.), px(20.)]);为什么 a b因为CornerRadius实现了FromValBorderRadius::all(vh(10.))先把vh(10.)转成CornerRadius { x: vh(10.), y: Auto }再与显式CornerRadius::circular(vh(10.))等价后者内部也是x: radius, y: Auto再叠加上面定制过的PartialEq二者自然相等。这是“绝大多数现有代码无需任何改动即可编译”的根本原因——px、vh、vw、percent、zero、auto等响应式值构造器Bevy UI 中的Val辅助函数都能无缝进入新的IntoCornerRadius参数槽。字段字面量写法结构体直接构造由于BorderRadius的字段本身就是公开的你仍然可以使用结构体字面量逐个角精细控制这也是官方示例中展示的最直观写法ui_node.rs#L2925-L2949fn 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.)), // 借助 FromVal也可直接 .into() top_right: percent(20.).into(), // 椭圆角水平 30px垂直 20px bottom_right: CornerRadius::new(px(30.), px(20.)), // 结构体字面量直接写 x / y bottom_left: CornerRadius { x: px(10.), y: px(40.) }, }, ..Default::default() }, BackgroundColor(BLUE.into()), )); }迁移指南给出的 Before / After 对比可以帮你更清晰地理解这一变化_release-content/migration-guides/border_radius.md// 迁移前字段是 Val BorderRadius { top_left: px(10.), top_right: percent(20.), bottom_right: zero(), bottom_left: vh(5.), } // 迁移后字段是 CornerRadius BorderRadius { top_left: CornerRadius::circular(px(10.)), top_right: CornerRadius::circular(percent(20.)), bottom_right: CornerRadius::circular(zero()), bottom_left: CornerRadius::circular(vh(5.)), } // 或借用 FromVal BorderRadius { top_left: px(10.).into(), top_right: percent(20.).into(), bottom_right: zero().into(), bottom_left: vh(5.).into(), }解析规则百分比基准、钳制与 ResolvedBorderRadius布局系统不会直接把Val交给渲染管线而是通过BorderRadius::resolveui_node.rs#L3211-L3249将其换算为物理像素得到ResolvedBorderRadius——此时四个角的字段已变为Vec2 { x, y }分别代表水平、垂直方向的半径这正是“字段升级为 2D 向量”的完整链路pub struct ResolvedBorderRadius { pub top_left: Vec2, // x: 水平半径, y: 垂直半径物理像素 pub top_right: Vec2, pub bottom_right: Vec2, pub bottom_left: Vec2, }解析时需注意以下几点百分比基准不同CornerRadius中 x 轴的百分比基于节点宽度解析y 轴百分比基于节点高度解析Val::resolve(scale_factor, size.x / size.y, ...)。这正是你能画出“宽圆高尖”椭圆角的原因。钳制规则任一轴解析结果都会被钳制在0.5 * 节点在该轴上的长度以内geometry.rs#L1420-L1427避免圆角弧线互相越界。若某角半径超过节点宽/高的一半会被自动当成“半宽/半高”处理这与 CSSborder-radius的直觉一致源码注释中亦直接引用了 MDN border-radius 作为参考语义。em / rem 支持resolve接收EmSize、RemSize与scale_factor因此CornerRadius同样支持Em/Rem/Vw/Vh/VMin等一切Val变体是真正意义上的响应式半径。圆形角的基准含Auto的单轴圆形角以节点最短边为解析基准并钳制到一半。例如在 100×50 的节点上CornerRadius::circular(px(100.))解析结果是(25, 25)而非(100, 100)测试用例见 geometry.rs#L1546-L1557。CornerRadius::resolve同样是解析的枢纽geometry.rs#L1388-L1428逻辑可用伪代码概括若 x Auto 且 y Auto - (0, 0) // 直角Default 也是直角 若 x Auto 或 y Auto - 圆形以 radius 沿节点短边解析clamp 到短边一半 否则 - 两轴分别按 x/size.x、y/size.y 解析整体 clamp 到半尺寸渲染侧原理GPU 上的椭圆距离场椭圆角最终由 GPU 上的 SDF有向距离场绘制。在sd_rounded_box中每个角都用一对vec4传入corner_radii_x四角的水平半轴与corner_radii_y四角的垂直半轴通过select_corner_radius根据采样点落在哪个象限选出对应角的二维半径再调用distance_to_ellipse_approx计算到椭圆边界的近似距离ui.wesl#L76-L88// 关键分支若某角任一轴半径 0退化为直角盒距离 if min(radius.x, radius.y) 0.0 { return straight_distance; } // 否则计算采样点到角上椭圆弧的有向距离 let inv_radii_sq 1.0 / (radius * radius); let corner_distance distance_to_ellipse_approx(q, inv_radii_sq, 1.0);因此一个四角全为ZERO的节点会完全跳过椭圆求交逻辑对老场景零额外开销只有真正使用圆角/椭圆角时才会进入椭圆距离计算。此外当节点携带图片背景ImageNode时渲染系统还会在 CPU 端按视觉盒子内缩量重新钳制圆角半径bevy_ui_render/src/lib.rs#L891-L899确保内缩后的图像裁切与外围圆角保持一致。破坏性变更清单与迁移提示结合发布说明与迁移指南从旧版升级时请留意以下四点BorderRadius结构体字面量的字段值需要从Val换成CornerRadius可用CornerRadius::circular(val)包裹或直接val.into()。二进制场景下x/y均为Val::Auto表示直角兼容旧的“无圆角”观感。构造器与更新函数不再 constBorderRadius::all、top_left、with_top_right等签名从Val改为impl IntoCornerRadius。绝大多数调用点传单个Val无需改动即可编译因为FromVal自动补全了圆形语义。BorderRadius::resolve_single_corner已移除需要解析单个角时请改用它内部的CornerRadius::resolvegeometry.rs#L1388。ResolvedBorderRadius字段从单个值变为Vec2如果渲染侧 / 自定义 UI 提取代码直接读取过resolved.top_left等字段并当作标量使用需要改为按.x/.y分别处理。在示例中验证与使用仓库 UI 示例大量采用新 API可直接作为你落地时的参考边框综合演示 examples/ui/styling/borders.rs 中用BorderRadius::MAX渲染完全圆角卡片阴影示例 examples/ui/styling/box_shadow.rs 在Node.border_radius上交替赋值ZERO/all(px(41))/MAX观察阴影随圆角变化按钮、滚动条、渐变等 UI 组件普遍使用BorderRadius::all(px(4.))、all(px(20.))这类写法如 examples/ui/widgets/button.rs、examples/ui/styling/gradients.rs它们都得益于FromVal而无需改动。实践建议设计一般的卡片、按钮、标签时优先使用CornerRadius::circular(...)或直接传Val圆形角、代码最简洁当需要胶囊按钮BorderRadius::MAX之外的非对称形态如“上圆下方”的通知条、异形头像框、Tab 标签的一角时再为对应角使用CornerRadius::new(x, y)组合出椭圆几何。由于整套 API 保留了对px/vh/percent/em等响应式值的支持配合窗口缩放也能稳定保持圆角比例真正把 CSS 中习以为常的圆角表现力带到了 Bevy UI 的数据驱动 ECS 工作流中。【免费下载链接】bevyA refreshingly simple>项目地址: https://gitcode.com/GitHub_Trending/be/bevy创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考