×

小程序里用canvas怎么绘制文字?从基础到进阶一次讲透

作者:Terry2025.11.03来源:Web前端之家浏览:33评论:0

小程序里用canvas怎么绘制文字?从基础到进阶一次讲透

不少做小程序开发的朋友,想在canvas里实现文字绘制,却被各种细节卡住——文字怎么显示?样式咋调整?长文本咋换行?今天咱就把小程序canvas绘文字的门道拆明白,从基础用法到进阶技巧,一步步说清楚。

小程序canvas绘制文字的基础步骤是啥?

想在小程序canvas里画文字,核心是用canvas上下文的绘制方法,先理清楚流程:

  1. 创建canvas上下文 → 小程序里用 wx.createCanvasContext(canvasId, this) 生成上下文对象(canvasId 要和wxml里canvas标签的id对应)。

  2. 设置文字样式 → 比如字体大小、颜色、对齐方式这些,靠上下文的属性设置,像 ctx.font = '16px 微软雅黑'ctx.fillStyle = '#333'

  3. 执行绘制命令 → 用 fillText(text, x, y) 画实心文字,或 strokeText(text, x, y) 画空心文字(轮廓)。

  4. 触发渲染 → 最后调用 ctx.draw() ,把之前的绘制操作真正渲染到canvas上。

举个简单例子,wxml里放canvas:

<canvas type="2d" id="myCanvas" style="width:300px;height:200px;"></canvas>

js里写绘制逻辑:

Page({
  onReady() {
    const ctx = wx.createCanvasContext('myCanvas', this)
    ctx.font = '20px PingFang SC' // 设置字体
    ctx.fillStyle = '#ff0000'     // 文字颜色
    ctx.fillText('这是测试文字', 50, 100) // 文字内容、x坐标、y坐标
    ctx.draw() // 渲染
  }
})

这样就能在canvas里看到红色的“这是测试文字”了。draw() 是异步的,要是后续还有绘制操作,得考虑用 draw(false, callback) 来叠加绘制,避免覆盖之前内容。

怎么给绘制的文字调样式?(字体、颜色、对齐方式)

基础绘制只能满足“有文字”,实际需求里得让文字好看、位置对,这就得调样式细节:

字体样式:font属性

和CSS的font语法差不多,要把字号、字体族等信息拼一起。ctx.font = 'bold 18px 宋体' ,顺序是「样式(可选,如bold/italic) 字号 字体名称」,注意小程序里要确保字体在手机里支持,优先用系统默认字体(像PingFang SCHarmonyOS Sans),避免因字体缺失显示异常。

文字颜色:fillStyle / strokeStyle

实心文字用 fillStyle ,空心轮廓用 strokeStyle ,值可以是纯色(如 #ff0000 )、渐变(后面讲特殊技巧)、图案,比如想让文字描边是蓝色,就写 ctx.strokeStyle = 'blue' ,再调 lineWidth 控制描边粗细,最后用 strokeText 绘制。

对齐方式:textAlign + textBaseline

这俩属性决定文字在(x,y) 坐标处的对齐规则:

  • textAlign 控制水平对齐,可选值 start(默认,按文字开始方向对齐,比如中文从左到右,x是文字左边缘)、 centerx是文字中心)、 endx是文字右边缘)、 left(同start)、 right(同end)。

  • textBaseline 控制垂直对齐,可选值 topy是文字顶部)、 hanging(悬挂基线,适合某些特殊字体)、 middley是文字中间)、 alphabetic(默认,字母基线,中文也基于这个)、 ideographic(表意文字基线,底部对齐)、 bottomy是文字底部)。

举个对齐的例子:想让文字在canvas水平居中、垂直居中,假设canvas宽300,高200,那x设150,y设100,

ctx.textAlign = 'center'
ctx.textBaseline = 'middle'
ctx.fillText('居中文字', 150, 100)

这样文字就稳稳待在canvas正中间了,不用自己算偏移量。

长文本自动换行咋实现?

小程序canvas的 fillText 默认只画一行,要是文字太长(比如一段介绍文案),就得自己拆分换行,核心思路是:把长字符串按每行能容纳的宽度分割,循环绘制每行

具体步骤:

  1. 测量文字宽度 → 用 ctx.measureText(text).width 能拿到指定样式下文字的宽度。

  2. 分割文本 → 从第一个字符开始,逐个累加,直到累加宽度超过canvas可用宽度,就把前面的作为一行,剩下的继续处理。

  3. 循环绘制每行 → 每行的y坐标递增(比如每行间隔24px),依次绘制。

代码示例(假设canvas宽度300,行高24px):

function drawMultiLineText(ctx, text, x, y, maxWidth, lineHeight) {
  let currentLine = '' // 当前行内容
  let words = text.split('') // 按字符拆分(如果是英文可按空格拆单词)
  for (let word of words) {
    let testLine = currentLine + word
    let testWidth = ctx.measureText(testLine).width
    if (testWidth > maxWidth && currentLine) { // 超过宽度且当前行有内容
      ctx.fillText(currentLine, x, y)
      currentLine = word
      y += lineHeight
    } else {
      currentLine = testLine
    }
  }
  // 处理最后一行
  if (currentLine) {
    ctx.fillText(currentLine, x, y)
  }
}
// 调用时:
const ctx = wx.createCanvasContext('myCanvas', this)
ctx.font = '16px PingFang SC'
drawMultiLineText(ctx, '这里是一段很长很长的测试文字,需要自动换行显示...', 20, 30, 260, 24)
ctx.draw()

这里要注意:如果是中英文混合,按字符拆分更稳妥;如果是纯英文,按空格拆单词体验更好(避免单词被截断)。measureText 依赖当前的font样式,所以拆分前要确保已经设置好字体,不然测量宽度会不准。

文字绘制时的性能坑怎么避?

要是做海报生成、动态图表这类文字多的场景,canvas绘制容易卡,得注意性能优化:

减少draw()调用次数

ctx.draw() 是把绘制命令批量渲染,频繁调用会触发多次重绘,所以尽量把所有绘制操作(文字、图形等)放在一次draw里,比如先把所有文字样式设置、位置计算好,再统一调draw,而不是画一行文字就draw一次。

缓存measureText结果

每次 measureText 都会触发内部计算,文字多的时候很耗性能,如果是固定样式的文字(比如标题、固定文案),可以把测量好的宽度存在变量里,避免重复测量。

用离屏canvas预处理(小程序2d canvas支持)

如果需要绘制复杂重复的文字元素(比如带样式的标签重复出现),可以先在离屏canvas里画好,再把离屏canvas当作图片画到主canvas上,不过小程序里离屏canvas的创建和使用要注意兼容,得先看基础库版本。

避免不必要的样式重设

比如循环绘制多行文字时,字体、颜色这些样式如果没变化,就别在循环里重复设置,把样式设置提到循环外面,减少上下文状态切换的开销。

优化示例:

Page({
  onReady() {
    const ctx = wx.createCanvasContext('myCanvas', this)
    // 统一设置样式
    ctx.font = '16px PingFang SC'
    ctx.fillStyle = '#333'
    ctx.textAlign = 'left'
    // 批量绘制内容
    drawMultiLineText(ctx, '长文本...', 20, 30, 260, 24)
    ctx.fillText('单独一行的标题', 20, 200)
    // 最后一次draw
    ctx.draw()
  }
})

这样所有绘制操作一次性渲染,比多次draw流畅很多。

有没有特殊场景的文字绘制技巧?(比如渐变文字、阴影文字)

普通文字满足不了设计需求时,这些技巧能让文字更炫酷:

渐变文字

思路是用线性渐变/径向渐变作为fillStyle,再绘制文字,步骤:

  • 创建渐变对象: const gradient = ctx.createLinearGradient(x0, y0, x1, y1) (线性渐变,四个参数是渐变起始和结束的坐标)。

  • 添加颜色节点: gradient.addColorStop(0, '#ff0000')gradient.addColorStop(1, '#00ff00') (0到1是渐变范围)。

  • 把渐变设为fillStylectx.fillStyle = gradient

  • 绘制文字: ctx.fillText('渐变文字', 50, 100)

代码示例(水平渐变文字):

const ctx = wx.createCanvasContext('myCanvas', this)
const gradient = ctx.createLinearGradient(0, 0, 200, 0)
gradient.addColorStop(0, 'red')
gradient.addColorStop(1, 'blue')
ctx.font = '30px 黑体'
ctx.fillStyle = gradient
ctx.fillText('渐变文字', 50, 100)
ctx.draw()

这样文字就会从红色渐变到蓝色,视觉效果拉满。

阴影文字

给文字加阴影,能让文字更立体,靠这几个属性:

  • shadowOffsetX :阴影水平偏移量(正负控制方向)。

  • shadowOffsetY :阴影垂直偏移量。

  • shadowBlur :阴影模糊程度(数值越大越模糊)。

  • shadowColor :阴影颜色(要带透明度的话用rgba)。

代码示例:

ctx.font = '24px 华文楷体'
ctx.shadowOffsetX = 2
ctx.shadowOffsetY = 2
ctx.shadowBlur = 4
ctx.shadowColor = 'rgba(0,0,0,0.3)'
ctx.fillText('带阴影的文字', 80, 120)
ctx.draw()

这样文字下方会有淡淡的阴影,质感瞬间提升,要注意,阴影是叠加在文字上的,如果多个文字元素都要阴影,要确保样式设置的范围,避免互相干扰。

常见错误排查:文字不显示、样式不对咋解决?

开发时遇到文字没出现、样式乱套,大概率是这些原因:

文字不显示

  • 没调 ctx.draw() :所有绘制操作后必须调draw才会渲染,漏了就看不到。

  • canvas尺寸不对:wxml里canvas的style宽度高度和实际绘制时的逻辑尺寸不一致,导致文字画到 canvas 外面,要确保style的宽高和canvas的实际像素尺寸匹配(小程序canvas默认是300x150,样式里要显式设置)。

  • 文字颜色和背景色一样:比如canvas背景是白色,文字fillStyle也设成白色,自然看不到,检查颜色值是否正确。

字体样式不生效

  • font属性格式错:必须包含字号和字体族,比如写成 ctx.font = '微软雅黑' (没写字号)就无效,得是 '16px 微软雅黑'

  • 字体不存在:手机里没装设置的字体(华文楷体”在部分安卓机可能没有),换成系统默认字体(如PingFang SC)试试。

对齐方式错乱

  • textAlign/textBaseline理解错:比如想让文字右对齐,设了 textAlign: 'right' ,但x坐标给的是canvas最左端,结果文字被画到canvas外面,要记住,textAlign是基于x坐标的对齐方式,右对齐时x应该是文字右边缘的位置。

  • 没在绘制前设置对齐属性:样式设置要在fillText/strokeText之前,不然不生效。

长文本换行失败

  • measureText没拿到正确宽度:因为设置fontmeasureText之后,导致测量的是默认字体的宽度,要确保measureText前已经设置好font

  • 拆分逻辑有漏洞:比如中英文混合时按单词拆分(用split(' ')),但中文没空格,导致整段文字被当成一个单词,无法拆分,这种情况要换成按字符拆分。

遇到问题时,先把canvas背景设成浅色(比如浅黄色),方便看文字位置;再逐一检查绘制步骤、样式设置、坐标计算,一般能定位到问题。

小程序canvas绘制文字,看着是个小功能,实际要考虑样式、排版、性能、特殊效果等方方面面,从基础的`fillText`调用,到样式微调、长文本换行,再到渐变阴影这类特效,每一步都得结合场景去试,多写demo,遇到问题拆分成“样式设置→坐标计算→绘制命令→渲染触发”这几个环节排查,基本都能解决,要是做海报生成、数据可视化这类重canvas的需求,把文字绘制的细节吃透,能少走很多弯路~

您的支持是我们创作的动力!
温馨提示:本文作者系Terry ,经Web前端之家编辑修改或补充,转载请注明出处和本文链接:
https://www.jiangweishan.com/article/canvaswenzihuizhi235.html

网友评论文明上网理性发言 已有0人参与

发表评论: