鸿蒙平台uni-app x canvas高清适配与常见踩坑实战总结
在 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()
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 起已支持该组件,且未使用时可被摇树,不必担心包体积。
Re: 鸿蒙平台uni-app x canvas高清适配与常见踩坑实战总结
感谢分享,这篇整理得很实用。高清屏适配这块总结得很清楚,dpr 放大 canvas 实际像素再 scale 回逻辑尺寸,确实是必须做的,不然高分屏文字和线条很容易发虚。ctx.reset() 会连 scale 一起清掉这个坑很关键,很多人 reset 完就忘了重新 scale。异步获取上下文跨平台更稳这点也认同,小程序不支持同步,统一走异步能少很多兼容问题。选型那块也讲得明白,复杂绘制、动画、导出图片用 canvas,只是矩形和文字用 DrawableContext 更轻量。roundRect 的四角参数、measureText 算背景宽度、setLineDash 空数组恢复实线这些细节都很适合实战。完整页面里 onReady 后异步创建上下文并缓存 renderCtx 和 canvasEl 的思路也很顺,后面做鸿蒙端 canvas 就按这个清单核对。Re: 鸿蒙平台uni-app x canvas高清适配与常见踩坑实战总结
感谢分享,这篇总结挺实在的。HarmonyOS 上用 uni-app x 画 canvas,dpr 适配确实是绕不过去的点,先按实际像素放大再 scale 回逻辑尺寸,这个思路很清楚,尤其提醒 reset 之后 scale 会被重置、必须重新调用,这个坑很容易忽略。异步获取上下文跨平台推荐也很有价值,同步接口小程序不支持这点对跨平台项目很关键。选型上复杂绘制、动画、导出图片用 canvas,简单矩形文字用 DrawableContext,边界说得明白。矩形、圆、文字、measureText 算背景宽度、虚线这些示例也都很实用。另外正文最后好像断在 let canvasEl 了,后面完整页面组织那块如果补全就更好了。Re: 鸿蒙平台uni-app x canvas高清适配与常见踩坑实战总结
楼主整理得很细,尤其高清适配这块很实用。以前只知道 canvas 模糊要乘 dpr,但没注意到 reset 会把 scale 一起重置,这个坑确实容易踩,清空后重绘一不小心又糊了。异步 createCanvasContextAsync 配 getCurrentInstance()!.proxy 的写法也记下了,跨平台项目确实要避开同步获取。选型那段也有帮助,简单矩形和文字用 DrawableContext,复杂绘制、动画和导出再上 canvas,思路更清楚。想问下,初始化时按 dpr 设好 canvas.width、height 并 scale 后,后面只是 clearRect 或重新绘制,不调用 reset 的话就不用再 scale 吧?另外首帖最后的 canvasEl 部分好像没贴完,期待完整页面组织思路。
页:
[1]