前言

一张手绘笔记风的信息图,主题是 HarmonyOS7 Canvas 文字绘制清楚的核心技巧。画面中心

很多人学 Canvas,最先想到的都是图形。但到了真实业务里,真正决定画面是不是“能用”的,常常不是方块和圆,而是字。你图形画得再顺,标题摆不齐、标签不清楚、数值看着别扭,这个画布照样不专业。

这篇就专门讲 Canvas 里的文字绘制。案例覆盖的内容很基础,但也正因为基础,特别适合拿来建立文字样式的控制感。

这个案例重点不在内容,在样式组合

页面上依次展示了几种文字效果:

一张 sketch-notes 风格的框架图,解释 HarmonyOS7 Canvas 文字样式控制

  • 普通填充文字
  • 描边文字
  • 粗体文字
  • 居中文字
  • 小字号辅助文字

你可以把这一页理解成一个最小文字样式实验台。重点不是这些文案写了什么,而是同一块画布里,字体、大小、颜色、对齐方式怎么配合使用。

完整代码

import { DEMO_BG_COLOR, DEMO_CARD_COLOR, DEMO_THEME_COLOR, DEMO_SUBTEXT_COLOR } from './types'

@Entry
@Component
struct CanvasTextStyling {
  @State isShow: boolean = true
  private settings: RenderingContextSettings = new RenderingContextSettings(true)
  private context: CanvasRenderingContext2D = new CanvasRenderingContext2D(this.settings)

  build() {
    Column() {
      if (this.isShow) {
        Scroll() {
          Column({ space: 16 }) {
            Text('Canvas 文字绘制').fontSize(16).fontColor(DEMO_THEME_COLOR).fontWeight(FontWeight.Bold)

            Column() {
              Text('文字绘制效果').fontSize(13).fontColor('#4D96FF').margin({ bottom: 8 })
              Column() {
                Canvas(this.context)
                  .onReady(() => {
                    let y = 30

                    this.context.font = '18px sans-serif'
                    this.context.fillStyle = '#4D96FF'
                    this.context.fillText('fillText 填充文字', 10, y)
                    y += 35

                    this.context.font = '20px sans-serif'
                    this.context.strokeStyle = '#FF6B6B'
                    this.context.lineWidth = 1
                    this.context.strokeText('strokeText 描边文字', 10, y)
                    y += 35

                    this.context.font = 'bold 22px sans-serif'
                    this.context.fillStyle = '#FFA500'
                    this.context.fillText('bold 粗体文字', 10, y)
                    y += 40

                    this.context.font = '14px sans-serif'
                    this.context.fillStyle = '#6BCB77'
                    this.context.textAlign = 'center'
                    this.context.fillText('textAlign: center 居中', 190, y)
                    y += 30

                    this.context.font = '12px sans-serif'
                    this.context.fillStyle = '#999999'
                    this.context.textAlign = 'left'
                    this.context.fillText('font: "12px sans-serif" 设置字体', 10, y)
                  })
                  .width('100%')
                  .height(200)
                  .backgroundColor('#F0F0F0')
                  .borderRadius(8)
              }
            }
            .backgroundColor(DEMO_CARD_COLOR)
            .padding(12)
            .borderRadius(12)

            Column({ space: 6 }) {
              Text('文字绘制 API').fontSize(14).fontColor(DEMO_THEME_COLOR).fontWeight(FontWeight.Bold)
              Text('context.font = "16px sans-serif" - 设置字体').fontSize(12).fontColor('#333333')
              Text('context.fillStyle = "#333" - 设置填充颜色').fontSize(12).fontColor('#333333')
              Text('context.fillText(text, x, y) - 填充文字').fontSize(12).fontColor('#333333')
              Text('context.strokeText(text, x, y) - 描边文字').fontSize(12).fontColor('#333333')
              Text('context.textAlign = "center"/"left"/"right" - 对齐').fontSize(12).fontColor('#333333')
              Text('context.textBaseline = "top"/"middle"/"bottom" - 基线').fontSize(12).fontColor('#333333')
              Text('context.measureText(text).width - 测量文字宽度').fontSize(12).fontColor('#333333')
            }
            .width('100%')
            .backgroundColor('#E8F4FD')
            .padding(14)
            .borderRadius(12)
          }
          .width('100%')
        }
      }

      Text('CanvasTextStyling - fillText 与 strokeText')
        .fontSize(12)
        .fontColor(DEMO_SUBTEXT_COLOR)
        .margin({ top: 12 })
    }
    .width('100%')
    .height('100%')
    .backgroundColor(DEMO_BG_COLOR)
    .padding(16)
  }
}

一张手绘流程图风格的配图,展示 Canvas 文字绘制在实际页面中的步骤。流程从“设置 Canvas

先搞清楚:文字也是画出来的

在普通 ArkUI 组件里,Text() 是声明式组件;但到了 Canvas 里,文字不是组件,而是一种绘制结果。

这意味着两件事。

第一,你得自己决定每一行字的坐标。

第二,样式不会自动分层,字号、粗细、颜色、对齐都要你手动控制。

案例里用一个 y 变量逐行推进:

let y = 30
this.context.fillText('fillText 填充文字', 10, y)
y += 35

这是最适合入门的写法,因为你能非常明确地看到“下一行为什么会跑到下面去”。

font 是整套文字样式的入口

案例几乎每画一段文字前,都会先改 font

this.context.font = '18px sans-serif'
this.context.font = '20px sans-serif'
this.context.font = 'bold 22px sans-serif'

这里别只把它当成字号设置。font 字符串本质上是一次性描述文字样式的入口,里面可以包含粗细、字号、字体族。

比如:

  • '18px sans-serif' 是普通 18 像素无衬线字体
  • 'bold 22px sans-serif' 是粗体 22 像素无衬线字体

如果你要在同一块画布里做标题、正文、说明、数值,这个属性会频繁切换。

fillText()strokeText() 的区别

案例把这两个方法并排演示,非常适合对比理解。

fillText()
this.context.fillStyle = '#4D96FF'
this.context.fillText('fillText 填充文字', 10, y)

这是最常用的文字绘制方式。它画出来的是实心字,适合绝大多数正文、标题、数值标签。

strokeText()
this.context.strokeStyle = '#FF6B6B'
this.context.lineWidth = 1
this.context.strokeText('strokeText 描边文字', 10, y)

这画出来的是轮廓字。它不适合大段正文,但在海报标题、装饰字、特殊提示上会有用。

你可以把它理解成一种更强调视觉风格的文字表现,而不是信息密度最高的那种文字。

textAlign 会直接影响你怎么找坐标

案例里有一段居中文字:

this.context.textAlign = 'center'
this.context.fillText('textAlign: center 居中', 190, y)

这句代码的重点,不只是把字居中,而是告诉你:当对齐方式变化后,传进去的 x 坐标代表的意义也变了。

  • left 时,x 是文字左边缘
  • center 时,x 是文字中心
  • right 时,x 是文字右边缘

如果你后面要做图表标题、柱状图标签、卡片中心文案,这个概念必须熟。

为什么案例最后又把 textAlign 改回 left

这一段其实特别像真实开发:

this.context.textAlign = 'left'
this.context.fillText('font: "12px sans-serif" 设置字体', 10, y)

因为 Canvas 上下文是有状态的。你一旦把 textAlign 改成 center,后面如果不改回来,接下来的文字都会继续按照居中逻辑去画。

这也是 Canvas 初学者最容易被坑的点之一:属性不是只影响当前一行,而是会保留到你下一次改掉它为止。

measureText() 为什么值得提前知道

案例虽然没有真的调用,但把这个 API 列出来了:

context.measureText(text).width

这个方法在真实项目里很实用。比如你要:

  • 动态计算标签宽度
  • 判断某段文字会不会超出画布
  • 让图表数值精确居中
  • 根据内容长度画背景气泡

只靠经验猜宽度,后面很容易翻车。能测量,就别拍脑袋。

这类画布文字最常见的使用场景

我最常见的几个场景是:

  • 图表标题和数值标签
  • 海报文案和价格展示
  • 进度面板里的状态说明
  • 签名板、标注板里的辅助文字

这些地方的共同点是,文字和图形不是分开的,而是要一起组成一个画面。所以你不能只会“显示文字”,还得会控制它和图形之间的关系。

容易踩的坑

第一个坑,是忘了不同样式之间要重新设置 font

第二个坑,是 textAlign 改了以后没恢复,后面的字全部歪掉。

第三个坑,是只关注字号,不关注行距,结果文字一多就挤成一团。

第四个坑,是在 Canvas 里还拿普通组件排版的思维来想,结果越画越乱。

写在最后

Canvas 里的文字绘制,说到底是在训练你对“视觉信息层级”的控制力。标题该大、说明该浅、标签该对齐、数值该稳,这些都不是默认送你的,而是你一笔一笔配出来的。

把这一页吃透,后面你再做图表、海报或者复杂状态面板,画面会一下子专业很多。

Logo

讨论HarmonyOS开发技术,专注于API与组件、DevEco Studio、测试、元服务和应用上架分发等。

更多推荐