ArkUI是一套UI開發框架,提供了開發者進行應用UI開發時所需具備的能力。隨著OpenAtom OpenHarmony(以下簡稱“OpenHarmony”)不斷更新迭代,ArkUI也提供了很多新的組件,例如Canvas、OffscreenCanvas、XComponent組件等。
新增的功能可以幫助開發者開發出更流暢、更美觀的應用。本篇文章將為大家分享如何通過Canvas組件實現涂鴉功能,用戶可以選擇空白畫布或者簡筆圖進行自由繪畫。效果展示
以下為效果圖:


相關代碼已經上傳至SIG倉庫,鏈接如下:
https://gitee.com/openharmony-sig/knowledge_demo_entainment/tree/master/FA/FreeDraw
目錄結構

源碼分析
一、Canvas組件介紹 本篇樣例主要利用ArkUI的Canvas組件實現涂鴉的功能,首先介紹一下Canvas組件。 Canvas組件主要包含了Canvas和CanvasRenderingContext2D,Canvas提供了畫布功能,CanvasRenderingContext2D提供了繪畫的屬性和方法。通過CanvasRenderingContext2D可以修改畫筆的樣色、粗細等屬性,從而畫出各式各樣的圖形。 以下是Canvas和CanvasRenderingContext2D在樣例開發中使用的相關接口信息。

Column() {
Text('選擇涂鴉的圖片:').margin('10vp').fontSize('30fp').fontColor(Color.Blue).height('5%')
Grid() {
ForEach(this.images, (item, index) => {
GridItem() {
Image(this.images[index])
.onClick((event) => {
router.push(
{
url: "pages/detailPage",
params: {
imgSrc: this.images[index],
},
}
)
})
.width('100%')
.height('100%')
.objectFit(ImageFit.Contain)
}
})
}
.padding({left: this.columnSpace, right: this.columnSpace})
.columnsTemplate("1fr 1fr 1fr") // Grid寬度均分成3份
.rowsTemplate("1fr 1fr") // Grid高度均分成2份
.rowsGap(this.rowSpace) // 設置行間距
.columnsGap(this.columnSpace) // 設置列間距
.width('100%')
.height('95%')
}
.backgroundColor(Color.Pink)
2. 涂鴉頁面 - 畫布Canvas的布局
通過Stack組件進行包裹,并將Canvas畫布覆蓋在選擇的背景圖片之上,這些背景圖片主要是水果簡筆畫。Stack() {
Image(this.imgSrc).width('100%').height('100%').objectFit(ImageFit.Contain)
Canvas(this.context)
.width('100%')
.height('100%')
// .backgroundColor('#00ffff00')
.onReady(() => {
})
.onTouch((event) => {
if (event.type === TouchType.Down) {
this.eventType = 'Down';
this.drawing = true;
[this.x, this.y] = [event.touches[0].x, event.touches[0].y];
this.context.beginPath();
this.context.lineCap = 'round';
if (this.isEraserMode) {
//橡皮擦模式
this.context.clearRect(this.x, this.y, 20, 20);
}
console.log('gyf Down');
}
if (event.type === TouchType.Up) {
this.eventType = 'Up';
this.drawing = false;
console.log('gyf Up!');
this.context.closePath();
}
if (event.type === TouchType.Move) {
if (!this.drawing) return;
this.eventType = 'Move';
console.log('gyf Move');
if (this.isEraserMode) {
//橡皮擦模式
this.context.clearRect(event.touches[0].x, event.touches[0].y, 20, 20);
} else {
this.context.lineWidth = this.lineWidth;
this.context.strokeStyle = this.color;
this.context.moveTo(this.x, this.y);
this.x = event.touches[0].x;
this.y = event.touches[0].y;
this.context.lineTo(this.x, this.y);
this.context.stroke();
}
}
})
}.width('100%').height('75%')
3.涂鴉頁面 - 畫筆設置區域的布局Column() {
Row() {
Text('粗細:')
Button('小').onClick(() => {
//設置畫筆的寬度
this.lineWidth = 5;
this.context.lineWidth = this.lineWidth;
this.isEraserMode = false;
console.log('gyf small button');
}).margin($r('app.float.wh_value_10'))
Button('中').onClick(() => {
//設置畫筆的寬度
this.lineWidth = 15;
this.context.lineWidth = this.lineWidth;
this.isEraserMode = false;
console.log('gyf middle button');
}).margin($r('app.float.wh_value_10'))
Button('大').onClick(() => {
//設置畫筆的寬度
this.lineWidth = 25;
this.context.lineWidth = this.lineWidth;
this.isEraserMode = false;
console.log('gyf big button');
}).margin($r('app.float.wh_value_10'))
Button('超大').onClick(() => {
//設置畫筆的寬度
this.lineWidth = 40;
this.context.lineWidth = this.lineWidth;
this.isEraserMode = false;
console.log('gyf super big button');
})
}.padding($r('app.float.wh_value_10')).margin($r('app.float.wh_value_5'))
//畫筆顏色
Scroll() {
Row() {
Text('顏色:')
Button(' ', { type: ButtonType.Circle })
.onClick(() => {
//黑色
this.color = '#000000';
this.context.strokeStyle = this.color;
this.isEraserMode = false;
console.log('gyf black button');
})
.backgroundColor('#000000')
.width('40vp')
.width('40vp')
.margin($r('app.float.wh_value_10'))
Button(' ', { type: ButtonType.Circle })
.onClick(() => {
//紅色
this.color = '#FF0000';
this.context.strokeStyle = this.color;
this.isEraserMode = false;
console.log('gyf red button');
})
.backgroundColor('#FF0000')
.width('40vp')
.width('40vp')
.margin($r('app.float.wh_value_10'))
Button(' ', { type: ButtonType.Circle })
.onClick(() => {
//綠色
this.color = '#00FF00';
this.context.strokeStyle = this.color;
this.isEraserMode = false;
console.log('gyf green button');
})
.backgroundColor('#00FF00')
.width('40vp')
.width('40vp')
.margin($r('app.float.wh_value_10'))
Button(' ', { type: ButtonType.Circle })
.onClick(() => {
//藍色
this.color = '#0000FF';
this.context.strokeStyle = this.color;
this.isEraserMode = false;
})
.backgroundColor('#0000FF')
.width('40vp')
.width('40vp')
.margin($r('app.float.wh_value_10'))
Button(' ', { type: ButtonType.Circle })
.onClick(() => {
//棕色
this.color = '#A52A2A';
this.context.strokeStyle = this.color;
this.isEraserMode = false;
})
.backgroundColor('#A52A2A')
.width('40vp')
.width('40vp')
.margin($r('app.float.wh_value_10'))
Button(' ', { type: ButtonType.Circle })
.onClick(() => {
//紫色
this.color = '#800080';
this.context.strokeStyle = this.color;
this.isEraserMode = false;
})
.backgroundColor('#800080')
.width('40vp')
.width('40vp')
.margin($r('app.float.wh_value_10'))
Button(' ', { type: ButtonType.Circle })
.onClick(() => {
//紫紅色
this.color = '#FF00FF';
this.context.strokeStyle = this.color;
this.isEraserMode = false;
})
.backgroundColor('#FF00FF')
.width('40vp')
.width('40vp')
.margin($r('app.float.wh_value_10'))
Button(' ', { type: ButtonType.Circle })
.onClick(() => {
//深藍色
this.color = '#00008B';
this.context.strokeStyle = this.color;
this.isEraserMode = false;
})
.backgroundColor('#00008B')
.width('40vp')
.width('40vp')
.margin($r('app.float.wh_value_10'))
Button(' ', { type: ButtonType.Circle })
.onClick(() => {
//深天藍
this.color = '#00BFFF';
this.context.strokeStyle = this.color;
this.isEraserMode = false;
})
.backgroundColor('#00BFFF')
.width('40vp')
.width('40vp')
.margin($r('app.float.wh_value_10'))
Button(' ', { type: ButtonType.Circle })
.onClick(() => {
//綠色
this.color = '#008000';
this.context.strokeStyle = this.color;
this.isEraserMode = false;
})
.backgroundColor('#008000')
.width('40vp')
.width('40vp')
.margin($r('app.float.wh_value_10'))
Button(' ', { type: ButtonType.Circle })
.onClick(() => {
//青綠色
this.color = '#32CD32';
this.context.strokeStyle = this.color;
this.isEraserMode = false;
})
.backgroundColor('#32CD32')
.width('40vp')
.width('40vp')
.margin($r('app.float.wh_value_10'))
Button(' ', { type: ButtonType.Circle })
.onClick(() => {
//橙色
this.color = '#FFA500';
this.context.strokeStyle = this.color;
this.isEraserMode = false;
})
.backgroundColor('#FFA500')
.width('40vp')
.width('40vp')
.margin($r('app.float.wh_value_10'))
Button(' ', { type: ButtonType.Circle })
.onClick(() => {
//黃色
this.color = '#FFFF00';
this.context.strokeStyle = this.color;
this.isEraserMode = false;
})
.backgroundColor('#FFFF00')
.width('40vp')
.width('40vp')
.margin($r('app.float.wh_value_10'))
}.padding('10vp')
}
.scrollable(ScrollDirection.Horizontal) // 設置滾動條水平方向滾動
.margin($r('app.float.wh_value_5'))
Row() {
Image('/common/images/eraser.png')
.onClick(() => {
//橡皮擦模式
this.isEraserMode = true;
console.log('gyf eraser button');
})
.width('50vp')
.height('50vp')
.margin('10vp')
Button('清理畫板').onClick(() => {
this.context.clearRect(0, 0, 1000, 1000);
})
}
.margin($r('app.float.wh_value_5'))
}
.width('100%')
.height('25%')
.alignItems(HorizontalAlign.Start)
三、邏輯代碼
邏輯代碼存在于Canvas的onTouch事件中,通過TouchType的Down、Up、Move來判斷開始、移動和結束的動作。一筆完整的繪制包含一次Down和Up,其中有若干次的Move。橡皮擦模式通過clearRect接口實現擦除的功能。.onTouch((event) => {
if (event.type === TouchType.Down) {
this.eventType = 'Down';
this.drawing = true;
[this.x, this.y] = [event.touches[0].x, event.touches[0].y];
this.context.beginPath();
this.context.lineCap = 'round';
if (this.isEraserMode) {
//橡皮擦模式
this.context.clearRect(this.x, this.y, 20, 20);
}
console.log('gyf Down');
}
if (event.type === TouchType.Up) {
this.eventType = 'Up';
this.drawing = false;
console.log('gyf Up!');
this.context.closePath();
}
if (event.type === TouchType.Move) {
if (!this.drawing) return;
this.eventType = 'Move';
console.log('gyf Move');
if (this.isEraserMode) {
//橡皮擦模式
this.context.clearRect(event.touches[0].x, event.touches[0].y, 20, 20);
} else {
this.context.lineWidth = this.lineWidth;
this.context.strokeStyle = this.color;
this.context.moveTo(this.x, this.y);
this.x = event.touches[0].x;
this.y = event.touches[0].y;
this.context.lineTo(this.x, this.y);
this.context.stroke();
}
}
})
總結
本文介紹了如何使用ArkUI框架提供的Canvas組件實現涂鴉功能。首先,通過Canvas的onTouch事件來跟蹤Down、Move和Up的事件,再設置CanvasRenderingContext2D的相關屬性并調用相關的方法,最終實現涂鴉的功能。除了文中分享的涂鴉樣例,開發者還可以通過拓展其他相關的屬性和方法,實現更多好玩的、高性能的樣例。-
Canvas
+關注
關注
0文章
20瀏覽量
11188 -
涂鴉
+關注
關注
0文章
14瀏覽量
4286 -
OpenHarmony
+關注
關注
28文章
3836瀏覽量
18211
原文標題:如何利用OpenHarmony ArkUI的Canvas組件實現涂鴉功能?
文章出處:【微信號:gh_e4f28cfa3159,微信公眾號:OpenAtom OpenHarmony】歡迎添加關注!文章轉載請注明出處。
發布評論請先 登錄
涂鴉標準模組MCU SDK開發流程 精選資料推薦
通過一個圓形抽獎轉盤演示HarmonyOS自定義組件的實現
canvas繪制“飛機大戰”小游戲,真香!
如何用canvas組件實現在JS UI上畫出連續的線條?
OpenHarmony 3.1 Beta 版本關鍵特性解析——ArkUI canvas組件
如何利用OpenHarmony ArkUI的Canvas組件實現涂鴉功能?
如何利用OpenHarmony ArkUI的Canvas組件實現涂鴉功能?
本周四晚19:00知識賦能第八期第3課丨涂鴉小游戲的實現
【直播回顧】OpenHarmony知識賦能第八期:手把手教你實現涂鴉小游戲
HarmonyOS/OpenHarmony應用開發-ArkTS畫布組件Canvas
手把手教你使用ArkTS中的canvas實現簽名板功能
canvas基礎繪制方法介紹

評論