新特性:字体与排版进阶 —— 自定义字体与可变字体(Variable Font)应用
文章目录前言一、静态字体 vs 可变字体1.1 静态字体的问题1.2 可变字体的优势二、注册自定义字体2.1 字体文件放置2.2 registerFont API2.3 应用到 Text2.4 注册失败回退三、fontWeight 与 enableVariableFontWeight3.1 默认行为阶梯字重3.2 可变字重模式3.3 Demo 对比实验四、字重动画animateTo State4.1 核心思路4.2 呼吸循环动画4.3 Watch 的作用4.4 字重边界钳制五、字重刻度一图看懂 wght 轴六、排版进阶参数6.1 letterSpacing字间距6.2 lineHeight行高6.3 组合使用6.4 其他进阶 API扩展阅读七、系统字体 vs 自定义字体八、四个 Demo 场景对照九、踩坑清单9.1 动画不生效字重不变9.2 registerFont 后仍显示系统字体9.3 中文显示为方块或回退9.4 fontWeight(350) 看起来和 400 一样9.5 动画卡顿9.6 字重过大文字被裁切十、生产实践建议10.1 字体选型10.2 字重动画使用场景10.3 与主题系统联动十一、完整代码脉络十二、总结前言品牌 App 的辨识度往往藏在细节里标题用什么字体、正文行高多少、数字是否等宽。系统默认字体够用但难以传递品牌气质。传统方案是打包多套静态字体——Regular.ttf、Medium.ttf、Bold.ttf……包体积膨胀切换字重时还可能「跳变」。可变字体Variable Font用单个文件封装wght字重等设计轴可在 100–900 之间连续调节为字重动画打下基础。HarmonyOS ArkUI 提供两条路径getUIContext().getFont().registerFont()—— 注册rawfile中的外部字体fontWeight(数值, { enableVariableFontWeight: true })—— 启用可变字重轴配合animateTo实现粗细平滑过渡。本文结合项目 Demo从字体注册、可变字重原理、动画实现到排版参数做一次完整实战解析。一、静态字体 vs 可变字体1.1 静态字体的问题字重传统方案包体积200Inter-ExtraLight.ttf80KB400Inter-Regular.ttf80KB700Inter-Bold.ttf80KB900Inter-Black.ttf80KB四套文件约 320KB且fontWeight(550)这类中间值无法精确渲染只能吸附到最近的静态文件。1.2 可变字体的优势Inter VariableDemo 所用单文件约 843KB覆盖wght轴 100–900一个 .ttf └── wght 轴100 ────────── 400 ────────── 900 ↑ 350、550、720 均可精确渲染配合enableVariableFontWeight: truefontWeight(350)不再吸附到 400而是沿字体设计轴插值——这是字重动画「细腻」的关键。二、注册自定义字体2.1 字体文件放置将.ttf/.otf放入entry/src/main/resources/rawfile/entry/src/main/resources/rawfile/ └── fonts/ └── InterVariable.ttfrawfile中的资源不编译进resources索引通过$rawfile(相对路径)访问适合较大字体文件。2.2 registerFont APIaboutToAppear():void{this.getUIContext()?.getFont()?.registerFont({familyName:InterVariable,familySrc:$rawfile(fonts/InterVariable.ttf)})}参数说明familyName注册后的字体族名供fontFamily()引用familySrc字体文件路径支持string或$rawfile()注意须通过getUIContext().getFont()获取Font实例再调用不能直接在全局调用。2.3 应用到 TextText(HarmonyOS).fontFamily(InterVariable).fontWeight(400,{enableVariableFontWeight:true}).fontSize(40)2.4 注册失败回退Demo 用try/catch包裹注册逻辑失败时回退HarmonyOS SansprivatefontFamilyName():string{returnthis.fontRegistered?InterVariable:HarmonyOS Sans}生产环境建议注册失败打日志UI 显示降级提示避免静默使用错误字体。三、fontWeight 与 enableVariableFontWeight3.1 默认行为阶梯字重不传enableVariableFontWeight时文档约定数字类型字重范围[100, 900]步进 100Text(阶梯字重).fontWeight(550)// 可能吸附到 500 或 600FontWeight枚举也只有Lighter / Normal / Medium / Bold等离散档位。3.2 可变字重模式constVAR_FONT_OPTIONS:FontSettingOptions{enableVariableFontWeight:true}Text(可变字重).fontFamily(InterVariable).fontWeight(550,VAR_FONT_OPTIONS)// 精确 550FontSettingOptions自 API 12 起支持API 24 还扩展了FontWeightConfigs含enableDeviceFontWeightCategory等设备字重类别联动。3.3 Demo 对比实验「可变 vs 阶梯」场景提供 Toggle 切换可变字重滑块拖到 350、550笔画粗细连续变化阶梯字重关闭enableVariableFontWeight中间值吸附到最近 100 档位可感知「跳跃感」。这是理解 Variable Font 价值最直观的实验。四、字重动画animateTo State4.1 核心思路StateWatch(onWeightChanged)animatedWeight:number350privateplayWeightAnim():void{this.animatedWeight200this.getUIContext()?.animateTo({duration:2200,curve:Curve.EaseInOut},():void{this.animatedWeight800})}animateTo在 2200ms 内将animatedWeight从 200 插值到 800。因绑定了Text().fontWeight(this.animatedWeight, VAR_FONT_OPTIONS)每一帧字重连续变化形成「由细变粗」的动画。4.2 呼吸循环动画privateplayBreathAnim():void{this.getUIContext()?.animateTo({duration:1400,curve:Curve.EaseInOut},(){this.animatedWeight720})setTimeout((){this.getUIContext()?.animateTo({duration:1400,curve:Curve.EaseInOut},(){this.animatedWeight280})},1450)}适合加载等待、语音播报强调、焦点引导等微动效。比缩放/透明度更克制品牌感更强。4.3 Watch 的作用StateWatch(onWeightChanged)animatedWeight:number350onWeightChanged():void{// 动画期间触发 UI 刷新}animateTo驱动State变化时Watch确保 Text 组件及时重绘。若动画不生效优先检查①animatedWeight是否绑定到fontWeight② 是否开启enableVariableFontWeight③ 字体是否为可变字体。4.4 字重边界钳制privateapplyWeight(weight:number):number{returnMath.min(Math.max(Math.round(weight),100),900)}Inter Variable 的wght轴通常支持 100–900超出范围可能导致渲染异常或回退。五、字重刻度一图看懂 wght 轴Demo「字重刻度」场景并排展示weight标签视觉200ExtraLight极细400Regular常规正文550Medium介于 Medium 与 Bold700Bold标题850Heavy超粗强调同一fontFamily只改fontWeight数值——这就是 Variable Font 在 UI 设计中的日常用法不必为每个字重维护一套样式表。六、排版进阶参数6.1 letterSpacing字间距Text(正文).fontFamily(InterVariable).letterSpacing(2)// 单位 vp增大舒展、高端时尚杂志标题减小/为 0紧凑、适合长文阅读。6.2 lineHeight行高Text(多行正文…).lineHeight(28)行高影响纵向节奏。可变字体 合适行高长文可读性显著提升。6.3 组合使用排版场景 Demo 将letterSpacing、lineHeight、headlineSize做成滑块标题用animatedWeight联动——一套参数控制标题气质适合运营配置或设计走查。6.4 其他进阶 API扩展阅读API作用lineSpacing(LengthMetrics)行间距API 12minLineHeight/maxLineHeight行高上下限API 22lineHeightMultiple行高倍数API 22fontStyle(FontStyle.Italic)斜体Variable Font 还可有slnt轴Demo 聚焦wght轴宽度wdth、倾斜slnt等轴需字体本身支持HarmonyOS 侧主要透传fontWeight等属性。七、系统字体 vs 自定义字体Demo 顶部对比卡片┌─────────────┬─────────────┐ │ 系统默认 │ Inter Variable │ │ HarmonyOS │ HarmonyOS │ │ (Medium) │ (wght 550) │ └─────────────┴─────────────┘同文案、不同字体族品牌差异一目了然。上线前建议确认字体授权Inter 为 SIL Open Font License可商用中文场景需额外注册中文字体如 Noto Sans SC VariableInter 仅覆盖拉丁字符包体积单可变字体通常 500KB–2MB比多套静态字体更省。八、四个 Demo 场景对照场景学习重点字重动画animateToenableVariableFontWeight可变 vs 阶梯滑块对比连续/吸附字重字重刻度200–850 并排展示排版进阶letterSpacing lineHeight 可变标题九、踩坑清单9.1 动画不生效字重不变未设enableVariableFontWeight: true字体不是 Variable Font普通 .ttf 只有固定字重animatedWeight未绑定到fontWeightfontFamily名称与registerFont的familyName不一致。9.2 registerFont 后仍显示系统字体aboutToAppear之前 Text 已渲染少见$rawfile路径错误字体文件损坏或未打包进 HAP。9.3 中文显示为方块或回退Inter Variable 不含中文字形中文会回退系统字体解决方案注册NotoSansSC-Variable.ttf等 CJK 可变字体。9.4 fontWeight(350) 看起来和 400 一样未开启enableVariableFontWeight或字体文件wght轴范围不包含 350。9.5 动画卡顿单页面大量 Text 同时做字重动画优化仅对标题级 1–2 个 Text 做动画正文静态。9.6 字重过大文字被裁切文档提示字重过大时文字可能被 clip给 Text 容器留足padding或增大lineHeight。十、生产实践建议10.1 字体选型拉丁为主Inter、Roboto Flex Variable中英混排Noto Sans SC Variable 拉丁 Variable Font 组合数字密集选用等宽或 tabular figures 支持的字体。10.2 字重动画使用场景场景推荐 weight 范围时长页面标题入场200 → 6001.5–2.5s加载呼吸280 ↔ 720 循环1.2–1.5s/单程按钮按下当前 100瞬间恢复 200msTab 切换强调400 → 700300–500ms避免所有文字都动画——克制是高级感来源。10.3 与主题系统联动深色模式下过细字重 300在 OLED 上可能发虚建议正文不低于 400标题可用 500–700 动画强调。十一、完整代码脉络// 1. 注册this.getUIContext()?.getFont()?.registerFont({familyName:InterVariable,familySrc:$rawfile(fonts/InterVariable.ttf)})// 2. 可变字重 TextStateanimatedWeight:number350Text(HarmonyOS).fontFamily(InterVariable).fontWeight(this.animatedWeight,{enableVariableFontWeight:true}).fontSize(40)// 3. 字重动画this.getUIContext()?.animateTo({duration:2200,curve:Curve.EaseInOut},(){this.animatedWeight800})三行核心 一个字体文件即可实现 iOS/Android 上常见的「字重呼吸」效果。十二、总结能力API要点外部字体registerFont$rawfilefamilyName与fontFamily一致可变字重enableVariableFontWeight: true需 Variable Font 文件字重动画animateToState weight100–900 连续插值排版letterSpacing/lineHeight与字重组合塑造层级一句话registerFont解决「用什么字体」enableVariableFontWeight解决「字重多细」animateTo解决「字重如何动」——三者组合让标题从「静态文字」变成「有生命力的品牌表达」。把 Demo 跑起来点「字重 200→800」看「HarmonyOS」四个字由细渐粗——那就是 Variable Font 在鸿蒙上的完整闭环。