在 HarmonyOS 上使用 uni-app x 开发时,只要涉及自定义图形、文字绘制,就离不开 canvas 组件。HarmonyOS 4.61 开始支持该组件。canvas 本身不显示内容,它是一个空白画布,真正绘制靠 JavaScript 调用 CanvasRenderingContext2D API。
先明确选型:uni-app x 有两套绘制方案。复杂绘制、动画、导出图片选 canvas;只画矩形、写文字,DrawableContext 更轻量。本文聚焦 canvas 基础绘制、上下文获取、高清屏适配和鸿蒙平台踩坑。
一、组件与上下文获取
基础声明:- <template>
- <canvas id="myCanvas" style="width: 300px; height: 200px;"></canvas>
- </template>
复制代码
获取上下文有异步和同步两种。跨平台推荐异步,HBuilderX 4.25+ 支持;同步仅支持 App 和 Web,HBuilderX 4.21+ 支持,小程序不支持,跨平台项目不要用。异步示例:- <script setup lang="uts">
- onReady(() => {
- uni.createCanvasContextAsync({
- id: 'myCanvas',
- component: getCurrentInstance()!.proxy,
- success: (context: CanvasContext) => {
- const ctx = context.getContext('2d')!
- const canvas = ctx.canvas
- const dpr = uni.getDeviceInfo().devicePixelRatio ?? 1
- canvas.width = canvas.offsetWidth * dpr
- canvas.height = canvas.offsetHeight * dpr
- ctx.scale(dpr, dpr)
- ctx.fillStyle = '#2196F3'
- ctx.fillRect(10, 10, 100, 100)
- }
- })
- })
- </script>
复制代码
注意:组合式写法中 component 参数用 getCurrentInstance()!.proxy,选项式写法用 this。
二、高清屏适配必须做
如果不处理 dpr,高分屏上会模糊。处理方式:- const dpr = uni.getDeviceInfo().devicePixelRatio ?? 1
- canvas.width = canvas.offsetWidth * dpr
- canvas.height = canvas.offsetHeight * dpr
- ctx.scale(dpr, dpr)
复制代码 原理是把 canvas 实际像素放大 dpr 倍,再通过 scale 缩放回逻辑尺寸。注意 ctx.reset() 会重置所有状态,包括 scale;reset 后要重新调用 scale(dpr, dpr),否则又会变模糊。
三、基础绘制场景
1. 矩形:fillRect 填充,strokeRect 描边,roundRect 圆角。roundRect 最后参数可传数字,也可传数组分别设置四个角。- ctx.fillStyle = '#2196F3'
- ctx.fillRect(10, 10, 120, 80)
- ctx.strokeStyle = '#F44336'
- ctx.lineWidth = 3
- ctx.strokeRect(150, 10, 120, 80)
- ctx.fillStyle = '#4CAF50'
- ctx.beginPath()
- ctx.roundRect(10, 110, 120, 80, 10)
- ctx.fill()
复制代码
2. 圆形:arc(x, y, r, start, end)。Math.PI * 2 是完整圆,Math.PI 是半圆。角度用弧度不是角度。- ctx.beginPath()
- ctx.arc(60, 60, 50, 0, Math.PI * 2)
- ctx.fill()
复制代码
3. 文字:fillText 填充文字,strokeText 描边文字,measureText 测量宽度。measureText 常用来计算标签或徽章背景尺寸。- ctx.font = '20px sans-serif'
- ctx.fillStyle = '#333333'
- ctx.fillText('Hello Canvas!', 10, 30)
- const metrics = ctx.measureText('带背景的文字')
- ctx.fillStyle = '#FF9800'
- ctx.fillRect(10, 130, metrics.width + 10, 24)
复制代码
4. 线条与虚线:setLineDash 设置虚线,传空数组恢复实线。- ctx.strokeStyle = '#F44336'
- ctx.setLineDash([5, 5])
- ctx.beginPath()
- ctx.moveTo(10, 70)
- ctx.lineTo(250, 70)
- ctx.stroke()
- ctx.setLineDash([])
复制代码
四、常用 CanvasRenderingContext2D API 速查
矩形:fillRect、strokeRect、clearRect、roundRect。
路径:beginPath、closePath、moveTo、lineTo、arc、quadraticCurveTo、bezierCurveTo、fill、stroke、clip。
文字:fillText、strokeText、measureText。
样式:fillStyle、strokeStyle、lineWidth、lineCap、lineJoin、font、textAlign、textBaseline、globalAlpha。
路径画完后,fill 填充内部,stroke 描边轮廓,两个可以同时用。
五、完整页面组织思路
一个基础绘制页面通常包括:scroll-view 中放 canvas、操作按钮和信息文本。按钮切换矩形、圆形、文字、线条、清空。脚本中缓存 renderCtx 和 canvasEl,onReady 后异步创建上下文并完成 dpr 处理,再调用 drawRect。核心初始化:- let renderCtx: CanvasRenderingContext2D | null = null
- let canvasEl: UniCanvasElement | null = null
- const infoText = ref('点击按钮切换绘制内容')
- onReady(() => {
- uni.createCanvasContextAsync({
- id: 'basicCanvas',
- component: getCurrentInstance()!.proxy,
- success: (context: CanvasContext) => {
- renderCtx = context.getContext('2d')!
- canvasEl = renderCtx!.canvas
- const dpr = uni.getDeviceInfo().devicePixelRatio ?? 1
- canvasEl!.width = canvasEl!.offsetWidth * dpr
- canvasEl!.height = canvasEl!.offsetHeight * dpr
- renderCtx!.scale(dpr, dpr)
- drawRect()
- }
- })
- })
复制代码
切换绘制前用 clearRect 清空,例如:- const clearCanvas = () => {
- if (renderCtx == null || canvasEl == null) return
- renderCtx!.clearRect(0, 0, canvasEl!.offsetWidth, canvasEl!.offsetHeight)
- infoText.value = '画布已清空'
- }
复制代码
六、鸿蒙平台特性
HarmonyOS 4.61 版本开始支持 canvas 组件。鸿蒙平台的 canvas 封装自鸿蒙自身 canvas 组件,性能表现良好;Android 默认开启硬件加速,鸿蒙也是。canvas 是独立模块,在鸿蒙平台占用几百 K 体积;如果不使用会被摇树摇掉,不会增加包体积。
与鸿蒙原生开发对比,原生写法类似:- Canvas(this.context)
- .width('100%')
- .height(200)
- .onReady(() => {
- this.context.fillStyle = '#2196F3'
- this.context.fillRect(10, 10, 100, 100)
- })
复制代码 uni-app x 的 canvas 使用 W3C 标准语法,Web 开发者更容易上手;鸿蒙原生 Canvas API 与 W3C 存在差异,需要适应。
七、踩坑清单
1. 必须在 onReady 后获取上下文,在 onLoad 或 onMount 中获取会失败。
2. 必须处理高清屏,否则高分屏模糊。
3. 同步方式 uni.getElementById 只支持 App 和 Web,不支持小程序,跨平台统一用 createCanvasContextAsync。
4. clearRect 只清除像素,不影响 fillStyle、strokeStyle 等样式;需要重置样式要手动设置。
5. 每次画新形状前调用 beginPath,否则新形状可能继承上一个路径样式。
6. 使用 ctx.reset 后,scale 会被重置,要重新执行 scale(dpr, dpr)。
总结
在鸿蒙平台用 uni-app x 画 canvas,推荐流程是:onReady 后调用 createCanvasContextAsync,getContext('2d') 拿到上下文,按 dpr 放大实际像素并 scale 回逻辑尺寸,然后按矩形、圆形、文字、线条等场景调用 CanvasRenderingContext2D API。简单绘制可选 DrawableContext,复杂绘制、动画和导出图片再上 canvas。HarmonyOS 4.61 起已支持该组件,且未使用时可被摇树,不必担心包体积。 |