Bevy UI 的 em/rem 字号单位:Val::Em、Val::Rem 迁移与实战指南(0.19 → 0.20)
【免费下载链接】bevyA refreshingly simple>项目地址: https://gitcode.com/GitHub_Trending/be/bevy
Bevy UI 在 0.20 中引入了em与rem两种相对字号单位,使Val的解析结果能够跟随字体大小缩放,这对"设计时定死像素、运行期调整字号"的可访问性与多设备适配场景非常有用。本文以官方迁移指南 val_em_and_rem.md 为主体,结合 release notes 与仓库源码,完整讲解新增的Val::Em/Val::Rem变体、受影响的resolve方法签名、EmSize组件的生命周期,以及GridTrack、FontSize::eval等周边 API 的迁移方式。
为什么需要 em 和 rem
em表示"当前节点字号的倍数",rem表示"根字号(全局基准字号)的倍数"。两者的数据来源不同:
em:相对于设置该值的节点自身的字号,由节点实体上的EmSize组件提供;rem:相对于全局的根字号,由既有的RemSize资源提供。
典型场景是:你希望 UI 在作者态下布局合理,但又能在运行期整体放大或缩小文字(例如无障碍功能,或在不同分辨率/设备上的适配)。若尺寸全部用px硬编码,改字号后布局会失衡;用em/rem表达,尺寸就能随字号一起伸缩。
官方给出的最小示例(bsn!语法):
bsn! { Node { width: em(10) } Text("Hello") TextFont { font_size: FontSize::Rem(1.5) } }另外注意一个行为变化:默认字号从px(20)变为rem(1)。如果你从不修改RemSize,这只是无副作用的等价替换;但一旦你调整了RemSize,默认文字会随之缩放。
Val 新增变体与字符串解析
在 geometry.rs 中,Val枚举新增了两个变体(见 L58-L67):
/// Set this value as a multiple of the node's own font size. /// /// `1em` is the node's font size, so `Val::Em(2.0)` is twice that. /// Resolved from the node's [`EmSize`] component. Em(f32), /// Set this value as a multiple of the root font size. /// /// Unlike [`Val::Em`] this ignores the node's own font size, so it means the same /// length anywhere in the hierarchy. Resolved from the [`RemSize`] resource. Rem(f32),Val::Em解析时读取节点自身的EmSize组件;Val::Rem解析时读取全局RemSize资源,因此在层级树任何位置含义都一致。
构造这两个变体的辅助函数em()和rem(),与既有的px()、percent()、vw()、vh()并列。Val还实现了FromStr(L89-L136),现在也能把"1.5rem"、"2em"之类的字符串解析为对应变体,这在从样式表/序列化数据构造Val时可以直接受益。
字号相关的类型定义位于 bevy_text/src/text.rs:
pub const DEFAULT_REM_SIZE_PX: f32 = 20.0; // L868 pub struct RemSize(pub f32); // L875 pub struct EmSize(pub f32); // L898DEFAULT_REM_SIZE_PX为 20.0,正好与 0.19 的默认字号px(20)保持一致,这也是默认字号改为rem(1)后行为等价的原因。
迁移点一:resolve 系列方法新增两个参数
由于解析Val现在需要"节点字号"和"根字号"两个基准,以下方法的签名都追加了em_size: EmSize与rem_size: RemSize两个参数:
Val::resolveVal2::resolveUiPosition::resolveCornerRadius::resolveRadialGradientShape::resolveUiTransform::compute_affineBorderRadius::resolve
调用方式的变化:
// 0.19 let physical = val.resolve(scale_factor, physical_base_value, physical_target_size)?; // 0.20 let physical = val.resolve( scale_factor, physical_base_value, physical_target_size, em_size, rem_size, )?;在源码中可以确认Val::resolve的新签名(geometry.rs L483-L488)以及Val2::resolve(L1190-L1195)都已带上em_size: EmSize(其后跟随rem_size)参数;UiTransform::compute_affine与RadialGradientShape::resolve同样如此(见 ui_transform.rs L69、gradients.rs L615)。
迁移点二:从 ComputedNode 取 em_size / rem_size
如果你是在布局完成之后、针对一个已存在的节点来解析Val(例如自定义box_shadow逻辑),不必自己去追踪字号——ComputedNode新增了两个字段,记录了排版时实际使用的字号值:
em_sizerem_size
它们的默认值为EmSize(DEFAULT_REM_SIZE_PX)与RemSize(DEFAULT_REM_SIZE_PX)(见 ui_node.rs L388-L389)。对已有节点做Val解析时,直接从该节点的ComputedNode上取这两个字段传入resolve即可。
迁移点三:Node 现在要求 EmSize 组件
这是 0.20 中最需要理解的一点:Node现在要求实体上存在EmSize(定义在bevy_text,并在bevy_ui::prelude中重导出),它作为该节点字号,供Val::Em解析使用。其生命周期规则如下:
- 节点带有
TextFont时:EmSize由TextFont派生;当TextFont、RemSize或渲染目标信息发生变化时会被重新计算。你手动设置的值会保留到下一次重算为止。 - 节点没有
TextFont时:EmSize完全由你自己设置,框架不会去动它;默认值为DEFAULT_REM_SIZE_PX(20px)。它与默认RemSize数值一致,但不会跟随RemSize的变化。 EmSize的层级传播是应用层的责任,bevy_ui不负责。也就是说,如果你希望子节点继承父节点的字号,需要自己在 App 里做传播系统。release notes 也明确写着:"EmSizeis derived fromTextFontwhen one is on the same entity; propagating it down the hierarchy is left to your app."
迁移点四:GridTrack 的 em/rem 构造器
网格轨道也支持了这两种单位,新增了构造函数与对应的 sizing function 变体:
GridTrack::em、GridTrack::remRepeatedGridTrack::em、RepeatedGridTrack::rem
它们底层使用新增的MinTrackSizingFunction::Em、MinTrackSizingFunction::Rem、MaxTrackSizingFunction::Em、MaxTrackSizingFunction::Rem变体。这两个变体同样出现在 ui_node.rs L1856-L1891 的 min/max sizing function 枚举中,用于按字号倍数定义网格轨道的上下限尺寸。
迁移点五:FontSize::eval 改为接收 RemSize
FontSize::eval的参数类型从裸f32变为RemSize结构体:
// 0.19 let size = font_size.eval(logical_viewport_size, rem_size_px); // 0.20 let size = font_size.eval(logical_viewport_size, RemSize(rem_size_px));这是一个类型层面的小改动:把根字号包装进RemSize后,字号求值的接口与布局解析接口的单位来源保持了一致的类型表达。
迁移检查清单
升级 0.19 → 0.20 时,可以按以下顺序排查编译错误:
- 找到所有
Val::resolve/Val2::resolve等调用,补上em_size与rem_size两个参数;针对已排版节点的解析,优先从ComputedNode的em_size/rem_size字段取值; - 为携带
Node的实体补上EmSize组件——有TextFont的节点会被框架自动派生/重算,没有TextFont的节点(如纯容器)需要自行设置默认值; - 检查 App 中是否需要新增
EmSize向下传播的系统(官方明确这是应用的责任); - 更新
FontSize::eval调用,把裸f32包成RemSize(f32); - 网格轨道需要字号单位时,改用
GridTrack::em/GridTrack::rem等构造函数; - 若有依赖"默认字号恒为 20px"的逻辑,注意默认字号现在是
rem(1)——只有在修改RemSize后才会与旧的硬编码 20px 产生差异。
小结
0.20 的 em/rem 支持让 Bevy UI 的尺寸系统从"像素与视口比例"扩展到了"字号比例":Val::Em随节点自身EmSize缩放,Val::Rem随全局RemSize缩放,且em/rem辅助函数、字符串解析、GridTrack构造器与bsn!语法均已就位。迁移成本集中在resolve系列方法的签名变化与EmSize组件的引入上;只要理解"EmSize由TextFont派生但层级传播交给应用"这一规则,升级路径就非常直接。相关源码入口:geometry.rs、ui_node.rs、text.rs、ui_transform.rs、gradients.rs。
【免费下载链接】bevyA refreshingly simple>项目地址: https://gitcode.com/GitHub_Trending/be/bevy
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考