简介

ArkUI是一套UI开发框架,提供了开发者进行利用UI开发时所需具备的能力。随着OpenAtom OpenHarmony(以下简称“OpenHarmony”)不断更新迭代,ArkUI也提供了很多新的组件,例如Canvas、OffscreenCanvas、XComponent组件等。新增的性能能够帮忙开发者开发出更晦涩、更好看的利用。本篇文章将为大家分享如何通过Canvas组件实现涂鸦性能,用户能够抉择空白画布或者简笔图进行自在绘画。

成果展现

以下为效果图:


首页显示了涂鸦的图片以及最初一张空白图片,在点击图片进入涂鸦页面后,能够对画笔的色彩、粗细进行设置。如果涂鸦过程中有谬误,能够用橡皮擦将画面擦除,也可点击革除按钮,清空涂鸦的内容,从新进行涂鸦操作。相干代码曾经上传至SIG仓库,链接如下:https://gitee.com/openharmony...

目录构造

源码剖析

一、Canvas组件介绍
本篇样例次要利用ArkUI的Canvas组件实现涂鸦的性能,首先介绍一下Canvas组件。Canvas组件次要蕴含了Canvas和CanvasRenderingContext2D,Canvas提供了画布性能,CanvasRenderingContext2D提供了绘画的属性和办法。通过CanvasRenderingContext2D能够批改画笔的样色、粗细等属性,从而画出各式各样的图形。以下是Canvas和CanvasRenderingContext2D在样例开发中应用的相干接口信息。

CanvasRenderingContext2D

二、剖析源码页面布局
第一个模块是首页布局,首页显示所有涂鸦蕴含的图片,点击图片能够进入页面;第二个模块是涂鸦模块,能够设置画笔的色彩、边条宽度等。

  1. 首页布局
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)
  1. 涂鸦页面 - 画布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的相干属性并调用相干的办法,最终实现涂鸦的性能。除了文中分享的涂鸦样例,开发者还能够通过拓展其余相干的属性和办法,实现更多好玩的、高性能的样例。