本文同步发表于我的微信公众号,扫一扫文章底部二维码或微信搜索 程语新视界 即可关注,每个工作日都有文章更新。
@Entry
@Component
struct CanvasExample {
private settings: RenderingContextSettings = new RenderingContextSettings(true) // 开启抗锯齿
private context: CanvasRenderingContext2D = new CanvasRenderingContext2D(this.settings)
build() {
Column() {
Canvas(this.context)//传入画笔
.width('100%')
.height(400)
.backgroundColor('#F0F4FD')
.onReady(() => { /* 绘制逻辑 */ })
}
}
}
关键点:
RenderingContextSettings
控制抗锯齿等渲染特性onReady
回调确保画布尺寸就绪 // 矩形(填充+描边)
context.fillStyle = '#4ECDC4'
context.fillRect(50, 50, 200, 150)
context.strokeStyle = '#556270'
context.strokeRect(45, 45, 210, 160)
// 圆形
context.beginPath()
context.arc(300, 120, 50, 0, 2 * Math.PI)
context.fill()
参数说明:
arc(x, y, 半径, 起始角, 结束角)
绘制圆弧路径 // 自定义路径
let path = new Path2D()
path.moveTo(100, 100)
path.lineTo(200, 150)
path.quadraticCurveTo(250, 200, 300, 180) // 二次贝塞尔曲线
context.stroke(path)
方法链:
beginPath()
开始新路径 → moveTo()
移动起点 → 绘制指令 → stroke()/fill()
完成绘制private offCanvas: OffscreenCanvas = new OffscreenCanvas(600, 600)
// 离屏绘制
const offContext = this.offCanvas.getContext("2d")
offContext.drawImage(imageSource, 0, 0, 600, 600)
// 合并到主画布
const bitmap = offCanvas.transferToImageBitmap()
context.transferFromImageBitmap(bitmap)
优势:
// 线性渐变
const grad = context.createLinearGradient(0, 0, 400, 0)
grad.addColorStop(0, '#E87361')
grad.addColorStop(1, '#BDDB69')
context.fillStyle = grad
context.fillRect(0, 0, 400, 400)
// 阴影效果
context.shadowBlur = 15
context.shadowColor = 'rgba(0,0,0,0.5)'
context.shadowOffsetX = 5
特效参数:
shadowBlur
控制模糊半径,shadowOffset
设置偏移量// 加载图片
const img = new Image()
img.src = 'common/images/logo.png'
img.onload = () => {
context.drawImage(img, 100, 100, 200, 200) // 缩放绘制
}
支持模式:
drawImage(image, dx, dy)
原始尺寸drawImage(image, dx, dy, dWidth, dHeight)
自定义尺寸context.font = 'bold 36px sans-serif'
context.textAlign = 'center'
context.fillStyle = '#333'
context.fillText('HarmonyOS', 200, 300)
文本属性:
textBaseline
控制基线对齐方式strokeText()
实现描边文字 beginPath
路径Canvas(this.context)
.onTouch((event: TouchEvent) => {
const x = event.touches[0].x
const y = event.touches[0].y
drawCircle(x, y) // 实时绘制触点
})
// 错误示例:未使用beginPath导致样式覆盖
ctx.strokeStyle = "blue";
ctx.moveTo(0, 0);
ctx.lineTo(100, 0);
ctx.stroke();
ctx.strokeStyle = "red";
ctx.lineTo(100, 100); // 红色会覆盖蓝色路径
ctx.stroke();
beginPath()
用于开始新的绘制路径或清空当前路径列表,确保后续绘制操作不与之前的路径叠加。3.路径生命周期管理
路径构建流程:beginPath()
→ 路径定义(如moveTo
/lineTo
) → stroke()
或fill()
→ 再次beginPath()
。
需为每个图形单独调用 beginPath
,避免路径干扰:
// 绘制两条独立线段(蓝色水平线 + 绿色斜线)
ctx.beginPath();
ctx.strokeStyle = "blue";
ctx.moveTo(20, 20);
ctx.lineTo(200, 20);
ctx.stroke();
ctx.beginPath(); // 重置路径
ctx.strokeStyle = "green";
ctx.moveTo(20, 20);
ctx.lineTo(120, 120);
ctx.stroke();
效果:两条线段颜色独立,互不影响
结合 closePath()
闭合路径,生成完整形状:
ctx.beginPath();
ctx.moveTo(50, 50);
ctx.lineTo(150, 50);
ctx.lineTo(150, 150);
ctx.lineTo(50, 150);
ctx.closePath(); // 闭合为矩形
ctx.strokeStyle = "#333";
ctx.stroke();
说明:closePath()
自动连接起点与终点,形成闭合路径。
在触摸事件中,每次绘制前需重置路径:
Canvas(this.context)
.onTouch((e: TouchEvent) => {
ctx.beginPath(); // 每次触摸开始新路径
ctx.arc(e.touches[0].x, e.touches[0].y, 10, 0, 2*Math.PI);
ctx.fill();
})
ctx.beginPath();
ctx.moveTo(50, 50);
ctx.lineTo(150, 50);
ctx.lineTo(150, 150);
ctx.fill(); // 未闭合时填充区域不确定
修复:添加 closePath()
或手动闭合路径。
若多个图形共享同一路径,最后一次设置的样式会覆盖之前的所有图形。
解决方案:每个图形独立使用 beginPath
+ 样式设置。
对象 | 关键方法/属性 | 说明 |
---|---|---|
CanvasRenderingContext2D | fillRect() , strokeText() , globalAlpha |
主绘制上下文 |
OffscreenCanvas | transferToImageBitmap() |
离屏画布对象 |
Path2D | arc() , quadraticCurveTo() |
路径定义对象 |
Image | onload , src |
图像加载对象 |
完整API文档可参考鸿蒙开发者文档中心,实际开发中建议结合离屏渲染与路径复用提升性能。