如何利用OpenHarmony ArkUI的Canvas组件实现涂鸦功能?

简介

ArkUI是一套UI开发框架,提供了开发者进行应用UI开发时所需具备的能力。随着OpenAtom OpenHarmony(以下简称“OpenHarmony”)不断更新迭代,ArkUI也提供了很多新的组件,例如Canvas、OffscreenCanvas、XComponent组件等。

新增的功能可以帮助开发者开发出更流畅、更美观的应用。本篇文章将为大家分享如何通过Canvas组件实现涂鸦功能,用户可以选择空白画布或者简笔图进行自由绘画。

效果展示

以下为效果图:

首页显示了涂鸦的图片以及最后一张空白图片,在点击图片进入涂鸦页面后,可以对画笔的颜色、粗细进行设置。如果涂鸦过程中有错误,可以用橡皮擦将画面擦除,也可点击清除按钮,清空涂鸦的内容,重新进行涂鸦操作。

目录结构

源码分析

一、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)

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的相关属性并调用相关的方法,最终实现涂鸦的功能。除了文中分享的涂鸦样例,开发者还可以通过拓展其他相关的属性和方法,实现更多好玩的、高性能的样例。

为了帮助到大家能够更有效的学习OpenHarmony 开发的内容,下面特别准备了一些相关的参考学习资料:

OpenHarmony 开发环境搭建:https://qr18.cn/CgxrRy

《OpenHarmony源码解析》:https://qr18.cn/CgxrRy

  • 搭建开发环境
  • Windows 开发环境的搭建
  • Ubuntu 开发环境搭建
  • Linux 与 Windows 之间的文件共享
  • ……

系统架构分析:https://qr18.cn/CgxrRy

  • 构建子系统
  • 启动流程
  • 子系统
  • 分布式任务调度子系统
  • 分布式通信子系统
  • 驱动子系统
  • ……

OpenHarmony 设备开发学习手册:https://qr18.cn/CgxrRy

在这里插入图片描述

OpenHarmony面试题(内含参考答案):https://qr18.cn/CgxrRy

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.rhkb.cn/news/332131.html

如若内容造成侵权/违法违规/事实不符,请联系长河编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

网络安全技术心得体会

网络与信息安全技术心得体会 通过对网络安全这门课程的学习,我进一步了解了网络安全技术的相关知识。大致来说,所谓网络安全指的是对网络系统中各类软硬件和数据信息等提供保护屏障,确保数据信息不受到恶意侵入、窃取等破坏,保证…

四. TensorRT模型部署优化-模型部署的基础知识

目录 前言0. 简介1. FLOPS2. TOPS3. HPC的排行,CPU/GPU比较4. FLOPs5. FLOPS是如何计算的6. CUDA Core vs Tensor Core总结参考 前言 自动驾驶之心推出的 《CUDA与TensorRT部署实战课程》,链接。记录下个人学习笔记,仅供自己参考 本次课程我们…

蓝桥杯物联网竞赛_STM32L071_17_DMA收发 不定长DMA接收

前言: 前面已经说过,由于国赛的代码量的增加,cpu在其他代码的时间块会较省赛大大增加,为了减少对cpu的依赖所以学习DMA收发数据 对于串口中断收发来说串口接收数据无法收取不定长数据所以不好用,而DMA有收集不定长数…

Android硬件渲染流程

Android硬件渲染流程 一.渲染流程1.VSync信号的监听2.VSync信号触发绘制 二.渲染原理1.画布的获取1.1 画布的创建1.2 渲染指令列表的创建 2.绘制与渲染指令2.1 矩形的绘制2.2 硬件渲染指令2.3 节点的绘制 3.绘制的提交3.1 绘制结果的保存3.2 绘制结果的获取 4.层级的构建4.1 绘…

解决Vue3+TS+vite,VSCode 高亮语法错误

一般像这种提示,有可能就是TypeScript语法的识别问题, 一般我们重装一下Vue - Official插件 或者将tcconfig.json中的moduleResolution改为node模式, 基本都是TypeScript无法识别vue文件中的TypeScript语句导致的

单片机原理及技术(二)—— AT89S51单片机(一)(C51编程)

目录 一、AT89S51单片机的片内硬件结构 二、AT89S51的引脚功能 2.1 电源及时钟引脚 2.2 控制引脚 2.3 并行 I/O口引脚 三、AT89S51的CPU 3.1 运算器 3.1.1 算术逻辑单元(ALU) 3.1.2 累加器A 3.1.3 程序状态字寄存器(PSW&#xff09…

JRT1.7发布

JRT1.7连仪器在线演示视频 JRT1.5实现质控主体、1.6基本完成质控;本次版本推进到1.7,1.7集菜单权限、登录、打印导出客户端、初始化、质控、Linux客户端、仪器连接和监控体系各种功能大全,上十年写系统用到的都全了。 这次直接挑战检验最难…

【微服务】springboot 构建镜像多种模式使用详解

目录 一、前言 二、微服务常用的镜像构建方案 3.1 使用Dockerfile 3.2 使用docker plugin插件 3.3 使用docker compose 编排文件 三、环境准备 3.1 服务器 3.2 安装JDK环境 3.2.1 创建目录 3.2.2 下载安装包 3.2.3 配置环境变量 2.2.4 查看java版本 3.3 安装maven …

加拿大媒体广告投放:媒体宣发主流媒体《金融邮报》《埃德蒙顿日报》

介绍《埃德蒙顿日报》与《埃德蒙顿太阳报》 在加拿大阿尔伯塔省首府埃德蒙顿,有两份主流新闻类报纸。其中,《埃德蒙顿日报》是加拿大主要英文报纸之一,也被称为爱蒙顿新闻报。而另一份报纸则是《埃德蒙顿太阳报》,是加拿大阿尔伯…

Linux操作系统最著名的两大系列Red Hat和Debian

Linux操作系统可以根据其背后的项目或社区分为不同的系列,其中最著名的两大系列是Red Hat系列和Debian系列。 1.著名的两大系列是Red Hat和Debian Red Hat系列: Red Hat Enterprise Linux (RHEL):这是Red Hat公司推出的企业级操作系统&#…

香橙派AIpro(OrangePi AIPro)开发板初测评

开发板简介 最近,我拿到手一款Orange Pi AI Pro 开发板,它是香橙派联合华为精心打造的高性能AI 开发板,最早发布于2023年12月,其搭载了昇腾AI 处理器,可提供8TOPS INT8 的计算能力,内存提供了8GB 和16GB两…

【C++】---多态

【C】---多态 一、多态的概念二、多态的定义及实现1、构成多态的2个必要条件2、什么叫做虚函数的重写?3、虚函数重写的3个例外4、建议把 析构函数 都定义为:虚函数 三、C11的两个关键字:final override1、final:修饰虚函数&#x…

Spring—Spring配置文件概念及应用(实现一个图形验证码)

文章目录 配置文件配置文件作用配置文件的格式配置文件优先级说明配置文件书写代码的格式yml文件代码的格式 Value注解 properties 缺点分析properties VS yml实现一个验证码程序 配置文件 配置文件作用 整个项目的重要信息我们都会配置在配置文件中,比如说我们数…

VMware虚拟机-设置系统网络IP、快照、克隆

1.设置网络IP 1.点击右上角开关按钮-》有线 已连接-》有线设置 2.手动修改ip 3.重启或者把开关重新关闭开启 2.快照设置 快照介绍: 通过快照可快速保存虚拟机当前的状态,后续可以使用虚拟机还原到某个快照的状态。 1.添加快照(需要先关闭虚拟机) 2.在…

电子商务网站(网上商店PetShop)

PetShop是一个范例,微软用它来展示.Net企业系统开发的能力。PetShop随着版本的不断更新,至现在基于.Net2.0的PetShop 4.0为止,整个设计逐渐变得成熟而优雅,有很多可以借鉴之处。PetShop是一个小型的项目,系统架构与代码…

【设计模式深度剖析】【2】【结构型】【装饰器模式】| 以去咖啡馆买咖啡为例 | 以穿衣服出门类比

👈️上一篇:代理模式 | 下一篇:适配器模式👉️ 目 录 装饰器模式定义英文原话直译如何理解呢?4个角色类图1. 抽象构件(Component)角色2. 具体构件(Concrete Component)角色3. 装饰&#xf…

【调试笔记-20240526-Linux-在 OpenWrt-23.05 发行版上安装 cpolar】

调试笔记-系列文章目录 调试笔记-20240526-Linux-在 OpenWrt-23.05 发行版上安装 cpolar 文章目录 调试笔记-系列文章目录调试笔记-20240526-Linux-在 OpenWrt-23.05 发行版上安装 cpolar 前言一、调试环境操作系统:Windows 10 专业版调试环境调试目标 二、调试步骤…

【找出第 K 大的异或坐标值】python

4层循环暴力超时 class Solution:def kthLargestValue(self, matrix: List[List[int]], k: int) -> int:nums[]for a in range(len(matrix)):for b in range(len(matrix[0])):num0for i in range(a1):for j in range(b1):num^matrix[i][j]nums.append(num)nums.sort()retu…

部门来了个测试开发,听说是00后,上来一顿操作给我看蒙了...

公司新来了个同事,听说大学是学的广告专业,因为喜欢IT行业就找了个培训班,后来在一家小公司实习半年,现在跳槽来我们公司。来了之后把现有项目的性能优化了一遍,服务器缩减一半,性能反而提升4倍&#xff01…

Docker自定义镜像

镜像 镜像包含了应用程序、程序运行的系统函数库、运行配置等文件的文件包。构建自定义镜像就是把上述文件打包的过程。 镜像结构 入口(entrypoint):镜像运行入口,一般是程序的启动脚本和参数 层(layer)&am…