一、前言
DevEco Studio版本:4.0.0.600
前些天写了一篇 鸿蒙自定义控件实现罗盘数字时钟效果 的文章,有同学私信说能不能介绍鸿蒙中的画布组件,下面文章介绍下鸿蒙中的Canvas画布、CanvasRenderingContext2D绘制组件,实现绘制文本、矩形、线条、圆形、椭圆、三角形、扇形、图片等。
Canvas:提供画布组件,用于自定义绘制图形。
CanvasRenderingContext2D:使用RenderingContext在Canvas组件上进行绘制,绘制对象可以是矩形、文本、图片等,相当于画笔
RenderingContextSettings:用来配置CanvasRenderingContext2D对象的参数,包括是否开启抗锯齿
参考链接:OpenHarmony CanvasRenderingContext2D
二、实现效果
三、具体实现逻辑
1、初始化
private settings: RenderingContextSettings = new RenderingContextSettings(true)
private context: CanvasRenderingContext2D = new CanvasRenderingContext2D(this.settings)
private img: ImageBitmap = new ImageBitmap("images/startIcon.png")
build() {
Stack({ alignContent: Alignment.Center }) {
Canvas(this.context)
.padding({ top: 76 })
.width('100%')
.height('100%')
.onReady(() => {
this.drawCanvas()
})
}
.width('100%')
.height('100%')
}
//画布逻辑
private drawCanvas(): void {
this.context.fillStyle = '#0080DC' //画笔填充颜色
this.context.strokeStyle = '#0080DC' //画笔线条颜色
this.context.font = '22px' //字体大小
}
2、画文字
//画文字
this.context.fillText("Hello World!", 10, 10)
3、画矩形
//画矩形
this.context.strokeRect(10, 30, 100, 100)
4、画线条
//画线
this.context.beginPath()
this.context.moveTo(10, 150)
this.context.lineTo(180, 200)
this.context.stroke()
5、画圆形
//画圆形
this.context.beginPath()
this.context.arc(60, 250, 50, 0, 2 * Math.PI)
this.context.stroke()
6、画椭圆形
//画椭圆形
this.context.beginPath()
this.context.ellipse(100, 360, 50, 100, Math.PI * 0.5, 0, Math.PI * 2)
this.context.stroke()
7、画三角形
先画两条线,然后通过closePath()方法实现闭环,依次达到画三角形效果
//画三角形
this.context.beginPath()
this.context.moveTo(10, 500)
this.context.lineTo(60, 420)
this.context.lineTo(120, 500)
this.context.closePath()
this.context.stroke()
8、画扇形
先画弧线,在画两条基于弧线起点和终点的线,依次来达到画扇形的效果
//画扇形
this.context.beginPath()
this.context.arc(110, 620, 100, Math.PI, 1.75 * Math.PI)
this.context.moveTo(10, 620)
this.context.lineTo(110, 620)
this.context.lineTo(180.71, 549.29)
this.context.stroke()
9、画图片
//画图片
this.context.drawImage(this.img, 0, 0, 144, 144, 150, 0, 144, 144)
10、画二阶贝赛尔曲线的路径
原理演示动画:
效果:
代码实现:
import display from '@ohos.display'
@Entry
@Component
struct Index {
private settings: RenderingContextSettings = new RenderingContextSettings(true)
private context: CanvasRenderingContext2D = new CanvasRenderingContext2D(this.settings)
private mDisplayWidth: number
private mDisplayHeight: number
aboutToAppear() {
this.getSize()
}
// 获取设备宽高计算表盘大小
async getSize() {
let mDisplay = await display.getDefaultDisplay()
this.mDisplayWidth = mDisplay.width
this.mDisplayHeight = mDisplay.height
}
build() {
Stack({ alignContent: Alignment.Center }) {
Canvas(this.context)
.padding({ top: 76 })
.width('100%')
.height('100%')
.onReady(() => {
this.drawCanvas()
})
.onTouch((event) => this.touchEvent(event))
}
.width('100%')
.height('100%')
}
private drawCanvas(): void {
this.context.clearRect(0, 0, this.mDisplayWidth, this.mDisplayHeight)
this.context.strokeStyle = '#0080DC' //画笔线条颜色
this.context.lineWidth = 3
this.context.font = '22px' //字体大小
//画第一个圆
this.context.beginPath()
this.context.arc(20, 200, 5, 0, 2 * Math.PI)
this.context.stroke()
//画第二个圆
this.context.beginPath()
this.context.arc(305, 200, 5, 0, 2 * Math.PI)
this.context.stroke()
//画贝塞尔曲线
this.context.beginPath()
this.context.moveTo(20, 195)
this.context.quadraticCurveTo(this.eventX, this.eventY, 300, 200)
this.context.stroke()
}
@State eventX: number = 0
@State eventY: number = 500
touchEvent(event: TouchEvent) {
switch (event.type) {
case TouchType.Down: // 手指按下
case TouchType.Move: // 手指移动
this.eventX = event.touches[0].x
this.eventY = event.touches[0].y
this.drawCanvas()
break
}
}
}
11、画三阶贝赛尔曲线的路径
原理演示动画:
效果:
代码实现:
import display from '@ohos.display'
@Entry
@Component
struct Index {
private settings: RenderingContextSettings = new RenderingContextSettings(true)
private context: CanvasRenderingContext2D = new CanvasRenderingContext2D(this.settings)
private mDisplayWidth: number
private mDisplayHeight: number
@State isFirst: boolean = true
aboutToAppear() {
this.getSize()
}
// 获取设备宽高计算表盘大小
async getSize() {
let mDisplay = await display.getDefaultDisplay()
this.mDisplayWidth = mDisplay.width
this.mDisplayHeight = mDisplay.height
}
build() {
Stack({ alignContent: Alignment.Center }) {
Canvas(this.context)
.padding({ top: 76 })
.width('100%')
.height('100%')
.onReady(() => {
this.drawCanvas()
})
.onTouch((event) => this.touchEvent(event))
Button(`点击切换,当前${this.isFirst ? '第一点' : '第二点'}`)
.onClick(() => {
this.isFirst = !this.isFirst
})
}
.alignContent(Alignment.TopStart)
.width('100%')
.height('100%')
}
private drawCanvas(): void {
this.context.clearRect(0, 0, this.mDisplayWidth, this.mDisplayHeight)
this.context.strokeStyle = '#0080DC' //画笔线条颜色
this.context.lineWidth = 3
this.context.font = '22px' //字体大小
//画第一个圆
this.context.beginPath()
this.context.arc(20, 200, 5, 0, 2 * Math.PI)
this.context.stroke()
//画第二个圆
this.context.beginPath()
this.context.arc(405, 200, 5, 0, 2 * Math.PI)
this.context.stroke()
//画贝塞尔曲线
this.context.beginPath()
this.context.moveTo(20, 195)
this.context.bezierCurveTo(this.eventFirstX, this.eventFirstX, this.eventSecondX, this.eventSecondY, 400, 200)
this.context.stroke()
this.context.stroke()
}
@State eventFirstX: number = 120
@State eventFirstY: number = 50
@State eventSecondX: number = 200
@State eventSecondY: number = 500
touchEvent(event: TouchEvent) {
switch (event.type) {
case TouchType.Down: // 手指按下
case TouchType.Move: // 手指移动
if (this.isFirst) {
this.eventFirstX = event.touches[0].x
this.eventFirstY = event.touches[0].y
} else {
this.eventSecondX = event.touches[0].x
this.eventSecondY = event.touches[0].y
}
this.drawCanvas()
break
}
}
}