HarmonyOS ArkGraphics 2D 文本绘制实操:用 TextBlob 做动态文字排版

这次做一个能直接看到效果的文字排版工坊:同一块 Canvas 里切换霓虹标题、逐字节奏和测量台。标题会在扫描光束和轨道环之间入场,汉字沿发光波形线跳动,测量台把文字边界框、宽度和 FontMetrics 画出来。
下面两段动态 GIF 和静态步骤图均来自已经完成的 Mate 60 Pro 实机操作。

先看最终效果

两段 GIF 都是从实体 Mate 60 Pro 连续抓帧后裁出 Canvas 区域制作:24 帧、8 FPS、3 秒、720×610,不是静态图重复播放。

进入实验 22 后,页面先运行"霓虹标题"。切换"逐字节奏",六个汉字逐字生成 TextBlob 并在基线附近做错峰运动;切换"测量台",可以直接看到文字的测量框、宽度、字形数量和 ascent/descent 数值。

本次要完成什么

  • drawing.Font 设置字号、抗锯齿和粗体。
  • TextBlob.makeFromString() 创建可绘制的文字对象。
  • Canvas.drawTextBlob() 把文字画在 ArkGraphics 2D Canvas 上。
  • Font.measureText()countText()getMetrics() 支撑排版,而不是凭经验写死位置。
  • 用一个 Animator 驱动标题入场和逐字波动,暂停后验证绘制计数不再增长。

准备

  • DevEco Studio 及 API 26 工程。
  • 已连接的 HarmonyOS 真机;本次页面不申请相机、相册、网络或存储权限。
  • 工程入口:entry/src/main/ets/features/graphics2d/TextTypographyPage.ets

参考:HarmonyOS ArkGraphics 2D 官方文档

实施过程

1. 从首页进入实验 22

在首页点击"实验 22:GlyphFlow 动态文字排版工坊"。页面顶部显示 GLYPHFLOW实验 22 · ArkGraphics 2D,中间大画布是实时绘制区域。

实验页截图同时记录了入口和默认的霓虹标题场景,后续切换场景都在同一页面完成。

2. 绘制霓虹标题

页面使用 RenderNode.draw() 获取 drawing.Canvas,先清理深色背景,再按顺序绘制基线、光晕层、主标题和字幕。光晕不是图片,而是对同一个 TextBlob 做偏移绘制。

ArkTS/ets 复制代码
private drawText(canvas: drawing.Canvas, text: string, font: drawing.Font,
  x: number, y: number, color: number): void {
  const blob: drawing.TextBlob = drawing.TextBlob.makeFromString(
    text, font, drawing.TextEncoding.TEXT_ENCODING_UTF8);
  const brush: drawing.Brush = new drawing.Brush();
  brush.setColor(color);
  brush.setAntiAlias(true);
  canvas.attachBrush(brush);
  canvas.drawTextBlob(blob, x, y);
  canvas.detachBrush();
}

同一标题先在 (x + 4, y + 5) 用低透明度颜色画一层,再在 (x, y) 画主色,最后用青色偏移两像素,得到可观察的光晕边缘。

霓虹标题真机结果见本节前的入口截图。

3. 切到逐字节奏

"逐字节奏"不把整句当作一块文字,而是逐个字符测量、逐个创建 TextBlob。每个字符的起点由前面字符的真实宽度累加,Y 坐标用相位和字符索引计算:

ArkTS/ets 复制代码
const rise: number = Math.sin(local * Math.PI * 2) * height * 0.055;
const x: number = cursor;
const y: number = centerY + rise;
this.drawText(canvas, text.charAt(index), font, x, y,
  index % 2 === 0 ? 0xFF6CE8FF : 0xFF9CFFE3);
cursor += widths[index];

这样可以单独控制每个字的延迟、颜色和位移,适合做标题入场、歌词、提示语和数据播报。

4. 在测量台查看真实指标

测量台固定绘制 MEASURE,边界框来自字体指标,而不是手工估计:

ArkTS/ets 复制代码
const textWidth: number = font.measureText(
  text, drawing.TextEncoding.TEXT_ENCODING_UTF8);
const glyphCount: number = font.countText(text);
const metrics: drawing.FontMetrics = font.getMetrics();
const top: number = baseline + metrics.ascent - 18;
const bottom: number = baseline + metrics.descent + 18;

画布下方同步显示 WIDTHGLYPHSA / D。字号从 42、54、68 px 切换,测量框和宽度会随之变化。

5. 调整字号、字重并暂停

点击 42 PX / 54 PX / 68 PX 可以重新创建字体并立即刷新画布;点击"字重:粗"切换 Font.enableEmbolden(true/false)。点击"暂停动画"后观察画布中的 绘制次数动画回调,等待几秒后再点"继续动画"。

点击"退出"后回到首页,退出路径会停止 Animator 并释放 RenderNode。

遇到的情况与处理

文字不显示

第一次验证时只创建了 TextBlob,没有给 Canvas 附加 Brush。修正为"创建 Brush → attachBrushdrawTextBlobdetachBrush"后,文字正常显示。

中文宽度不能用字符个数估算

中文和英文的字宽不同,逐字排版如果使用固定步长会出现重叠或间隙。现在每个字符都调用 Font.measureText(),用真实宽度累加 cursor

退出后动画仍在回调

页面退出统一执行 animator.cancel()controller.release()。RenderNode 的 dispose() 在退出路径中只执行一次,避免重进页面后残留上一轮动画。

真机日志

下面是本次实体真机采集的 Hilog 关键行。包名和进程号已去掉,只保留用于核对绘制、切换、暂停与释放的字段:

text 复制代码
TEXT_READY modes=3 durationMs=4200 expectedFps=60 permissionRequired=false
TEXT_DRAW mode=0 drawCount=1 width=995 height=995 textWidth=336 glyphs=4
TEXT_SWITCH mode=1 name=逐字节奏 phasePermille=675
TEXT_DRAW mode=1 drawCount=1800 width=1174 height=995 textWidth=294 glyphs=6
TEXT_SWITCH mode=2 name=测量台 phasePermille=236
TEXT_DRAW mode=2 drawCount=3180 width=1174 height=995 textWidth=264 glyphs=7
TEXT_SIZE sizePx=68 mode=2
TEXT_WEIGHT embolden=false
TEXT_PAUSE mode=2 phasePermille=569 drawCount=11445 callbacks=11440
TEXT_RESUME mode=2 phasePermille=569 drawCount=11445 callbacks=11440
TEXT_RESET mode=0 sizePx=54 embolden=true
TEXT_RELEASE reason=EXIT_BUTTON drawCount=11539 callbacks=11537

暂停截图显示"测量台已暂停 / PAUSED",日志中的暂停与恢复都落在同一 phasePermille=569;复位和退出分别记录了默认字号/字重以及 EXIT_BUTTON 释放路径。

验证结果

  • 三种场景均由 ArkGraphics 2D Font + TextBlob + Canvas.drawTextBlob() 实时绘制。
  • 字号、字重切换后,真机测量宽度和画布边界框同步变化。
  • 逐字节奏 GIF 使用 24 帧连续真机帧,画布中 6 个字的波形位置会变化。
  • 页面无需任何权限;日志确认退出时执行 TEXT_RELEASE,重新进入后从新的 TEXT_READY 开始。

本次文件

text 复制代码
entry/src/main/ets/features/graphics2d/TextTypographyPage.ets
scripts/check-text-typography.ps1
articles/第二十篇-ArkGraphics 2D文本绘制实战/
docs/qa/arkgraphics2d-20/

本次动态素材的原始帧在 docs/qa/arkgraphics2d-20/返工动态原始帧/,真机关键日志在 docs/qa/arkgraphics2d-20/真机验证日志.txt

相关推荐
搬砖的kk1 小时前
从 0 到 1:用 KMP + Compose Multiplatform + Ktor 实现鸿蒙「历史上的今天」应用
开源·harmonyos
星辰徐哥1 小时前
鸿蒙PC平台 Gnote 笔记应用适配实战:从 Linux 到 鸿蒙PC 的 Electron 迁移
linux·笔记·electron·harmonyos·gnote
Swift社区1 小时前
鸿蒙 App 如何设计 Memory Center?一文讲透 Agent 的长期记忆架构
华为·架构·harmonyos
binbin_521 小时前
HarmonyOS 应用功耗优化实战:定位耗电、收口任务与验证回归
android·回归·harmonyos
lqj_本人2 小时前
Flutter 三方库 Pedometer 的鸿蒙化适配指南
flutter·华为·harmonyos
AI备忘录21 小时前
(十九)华为华三锐捷迈普思科 交换机链路聚合配置命令(LACP/静态聚合五厂商对照)
运维·服务器·网络·网络协议·tcp/ip·华为
贾伟康1 天前
【时光清单|19】HarmonyOS ArkTS 回归测试实战:覆盖启动、空数据、异常输入和重复点击
软件测试·harmonyos·arkts·回归测试·hypium
坚果的博客1 天前
Flutter OHOS 环境搭建实战:oh-3.44.9-dev 从 0 到 1 完整记录
flutter·华为·harmonyos
坚果的博客1 天前
认识 CPF-RN:React Native 鸿蒙官方社区与四大核心仓库
react native·react.js·harmonyos