OpenHarmony 实战开发——ArkUI canvas组件
canvas 是 ArkUI 开发框架里的画布组件,常用于自定义绘制图形。因为其轻量、灵活、高效等优点,被广泛应用于 UI 界面开发中。本期,我们将为大家介绍 ArkUI 开发框架中 canvas 组件的使用。
一、canvas 介绍
1.1 什么是 canvas?
在 Web 浏览器中,canvas 是一个可自定义 width、height 的矩形画布,画布左上角为坐标原点,以像素为单位,水平向右为 x 轴,垂直向下为 y 轴,画布内所有元素都基于原点进行定位。如图 1 所示,我们通过 标签,创建了一个 width= 1500px,height=900px 的空白画布,我们还需要“画笔”才能绘制图形。canvas 采用轻量的逐像素渲染机制,以 JS 为“画笔”直接控制画布像素,从而实现图形绘制。
图 1 canvas 画布
1.2Canvas 的“画笔”
canvas 本身虽不具备绘制能力,但是提供了获取“画笔”的方法。开发者可通过 getContext(‘2d’) 方法获取 CanvasRenderingContext2D 对象完成 2D 图像绘制,或通过 getContext(‘webgl’) 方法获取 WebGLRenderingContext 对象完成 3D 图像绘制。
目前,ArkUI 开发框架中的 WebGL1.0 及 WebGL2.0 标准 3D 图形绘制能力正在完善中,所以本文将着重介绍 2D 图像的绘制。如图 2 所示,是 CanvasRenderingContext2D 对象提供的部分 2D 图像绘制方法,丰富的绘制方法让开发者能高效地绘制出矩形、文本、图片等。
图 2 图像绘制方法
除此之外,开发者还可以通过获取 OffscreenCanvasRenderingContext2D 对象进行离屏绘制,绘制方法同上。当绘制的图形比较复杂时,频繁地删除与重绘会消耗很多性能。这时,开发者可以根据自身的需求灵活选取离屏渲染的方式,首先通过创建 OffscreenCanvas 对象作为一个缓冲区,然后将内容绘制在 OffscreenCanvas 上,最后再将 OffscreenCanvas 绘制到主画布上,以提高画布性能,确保绘图的质量。
二、canvas 基础绘制方法
通过上节对 canvas 组件的基本介绍,相信大家对 canvas 组件已经有了一定的认识,下面我们将为大家实际演示 canvas 组件在 ArkUI 开发框架中的使用方法。ArkUI 开发框架参考了 Web 浏览器中 canvas 的设计,并在“类 Web 开发范式”及“声明式开发范式”两种开发范式中进行提供,接下来我们将分别介绍这两种开发范式中 canvas 的绘制方法。
2.1 类 Web 开发范式中 canvas 的绘制方法
类 Web 开发范式,使用 HML 标签文件进行布局搭建、CSS 文件进行样式描述,并通过 JS 语言进行逻辑处理。目前,JS 语言的 canvas 绘图功能已经基本上完善,下面我们将通过两个示例,展示基于 JS 语言的 canvas 组件基础使用方法。
2.1.1 矩形填充
CanvasRenderingContext2D 对象提供了 fillRect(x, y, width, height)方法,用于绘制一个填充的矩形。如图 3 所示,在画布内绘制了一个黑色的填充矩形,x 与 y 指定了在 canvas 画布上所绘制的矩形的左上角(相对于原点)的坐标,width 和 height 则设置了矩形的尺寸。
图 3 填充的矩形
示例代码如下:
//创建一个width=1500px,height=900px的画布
<!-- xxx.hml -->
<div><canvas ref="canvas" style="width: 1500px; height: 900px; "></canvas>
</div>
//xxx.js
export default {onShow() {const el =this.$refs.canvas;
//获取2D绘制对象const ctx = el.getContext('2d');
//设置填充为黑色ctx.fillStyle = '#000000';
//设置填充矩形的坐标及尺寸ctx.fillRect(200, 200, 300, 300);}
}
2.1.2 缩放与阴影
CanvasRenderingContext2D 对象提供了 scale(x,y) 方法,参数 x 表示横轴方向上缩放倍数,y 表示纵轴方向上缩放的倍数,值得注意的是缩放过程中定位也会被缩放。如图 4 所示,是将上个示例中的填充矩形通过 scale(2,1.5) 缩放,并通过 shadowBlur 方法加上阴影后的效果。
图 4 缩放与添加阴影后的效果
示例代码如下:
//xxx.js
export default {onShow() {const el =this.$refs.canvas;const ctx = el.getContext('2d');
//设置绘制阴影的模糊级别ctx.shadowBlur = 80;ctx.shadowColor = 'rgb(0,0,0)';ctx.fillStyle = 'rgb(0,0,0)';// x Scale to 200%,y Scale to 150%ctx.scale(2, 1.5);ctx.fillRect(200, 200, 300, 300);}
}
2.2 声明式开发范式中 canvas 的绘制方法
声明式开发范式,采用 TS 语言并进行声明式 UI 语法扩展,从组件、动效和状态管理三个维度提供了 UI 绘制能力,目前已经提供了 canvas 组件绘制能力,但功能仍在完善中。下面我们将通过两个示例展示声明式开发范式中 canvas 组件的基础使用方法。
2.2.1 图片叠加
如图 5 所示,是三张图片叠加的效果,顶层的图片覆盖了底层的图片。通过依次使用 drawImage(x,y, width, height) 方法设置图片坐标及尺寸,后面绘制的图片自动覆盖原来的图像,从而达到预期效果。
图 5 图片叠加
扩展的 TS 语言采用更接近自然语义的编程方式,让开发者可以直观地描述 UI 界面,示例代码如下:
@Entry
@Component
struct IndexCanvas1 {private settings:RenderingContextSettings = new RenderingContextSettings(true);
//获取绘图对象private ctx: RenderingContext = new RenderingContext(this.settings);
//列出所要用到的图片private img:ImageBitmap = new ImageBitmap("common/bg.jpg");build() {Column() {//创建canvasCanvas(this.ctx).width(1500).height(900).border({color:"blue",width:1,}).backgroundColor('#ffff00')//开始绘制.onReady(() => {this.ctx.drawImage( this.img,400,200,540,300);this.ctx.drawImage( this.img,500,300,540,300);this.ctx.drawImage( this.img,600,400,540,300);})}.width('100%').height('100%')}
}
2.2.2 点击创建线性渐变
如图 6 所示,是一个线性渐变效果。基于 canvas 扩展了一个 Button 组件,通过点击 “Click” 按钮,触发 onClick() 方法,并通过调用 createLinearGradient() 方法,绘制出了一个线性渐变色。
图 6 图片上添加文字
示例代码如下:
@Entry
@Component
struct GradientExample {private settings: RenderingContextSettings = new RenderingContextSettings(true);private context: RenderingContext = new RenderingContext(this.settings);private gra: CanvasGradient = new CanvasGradient();build() {Column({ space: 5 }) {
//创建一个画布Canvas(this.context).width(1500).height(900).backgroundColor('#ffff00 ')Column() {
//设置按钮的样式Button('Click').width(250).height(100).backgroundColor('#000000').onClick(() => {
//创建一个线性渐变色var grad = this.context.createLinearGradient(600, 200, 400, 750)grad.addColorStop(0.0, 'red');grad.addColorStop(0.5, 'white');grad.addColorStop(1.0, 'green');this.context.fillStyle = grad;this.context.fillRect(400, 200, 550, 550);})}.alignItems(HorizontalAlign.center)}}}
三、飞机大战小游戏绘制实践
如图 7 所示,是一款”飞机大战”小游戏,通过控制战机的移动摧毁敌机。如何使用 ArkUI 开发框架提供的 canvas 组件轻松实现这个经典怀旧的小游戏?实现思路及关键代码如下:
图 7 飞机大战小游戏
1. 首先列出游戏所用到的图片
private imgList:Array<string> = ["xx.png","xx.png"…];
2. 将图片渲染到 canvas 画布上
let img:ImageBitmap = new ImageBitmap("图片路径(如common/images)/"+this.imgList[数组下标]);
this.ctx.drawImage( img,150/* x坐标*/, 150/* y坐标*/, 600/*宽*/, 600/*高*/)
3. 绘制背景图片和战机向下移动的效果
this.ctx.drawImage(this.bg, 0, this.bgY);
this.ctx.drawImage(this.bg, 0, this.bgY - 480);
this.bgY++ == 480 && (this.bgY = 0);
4. 使用 Math.round 函数随机获取敌机图片并渲染到画布上,并且改变敌机 y 轴坐标,使它向下运动。
Efight = Math.round(Math.random()*7);
//前七张为敌机图片。
let img:ImageBitmap = new ImageBitmap("common/img"+this.imgList[Efight]);
this.ctx.drawImage(img, 0, this.Eheight + 50);//渲染敌机
5. 在页面每隔 120s 出现一排子弹,之后减小或增大(x,y)轴的坐标达到子弹射出效果。
let i= 0;
setInterval(()=>{this.ctx.drawImage(this.bulImg1,image.x – 10 – (i *10) , image.x + (i *10))this.ctx.drawImage(this.bulimg2, this. bulImg1,image.x – (i *10) , i image.x + (i *10))this.ctx.drawImage(this.bulimg3, image.x + 10 + (i *10), image.x + (i *10))
i ++;
},120)
6. 使用 onTouch 方法获取战机移动位置,获取拖动的坐标后重新设置战机的图片坐标,使战机实现拖动效果。
.onTouch((event)=>{var offsetX = event.localX ||event.touches[0].localX;var offsetY = event.localY ||event.touches[0].localY;var w = this.heroImg[0].width,h = this.heroImg[0].height;var nx = offsetX - w / 2,ny = offsetY - h / 2;nx < 20 - w / 2 ? nx = 20 - w / 2 : nx > (this.windowWidth - w / 2 - 20) ? nx =(this.windowWidth - w / 2 - 20) : 0;ny < 0 ? ny = 0 : ny > (this.windowHeight - h / 2) ? ny = (this.windowHeight –h/2) : 0;this.hero.x = nx;this.hero.y = ny;this.hero.count = 2;
注:本示例引用了部分开源资源,感兴趣的开发者可参考此开源资源,结合文中的实现思路补全代码。
为了帮助到大家能够更有效的学习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
相关文章:

OpenHarmony 实战开发——ArkUI canvas组件
canvas 是 ArkUI 开发框架里的画布组件,常用于自定义绘制图形。因为其轻量、灵活、高效等优点,被广泛应用于 UI 界面开发中。本期,我们将为大家介绍 ArkUI 开发框架中 canvas 组件的使用。 一、canvas 介绍 1.1 什么是 canvas? …...

js积累一(ipv4正则校验+弹窗方式)
1. ipv4地址,点分十进制的校验 var regexIP /^((25[0-5]|2[0-4]\d|((1\d{2})|([1-9]?\d)))\.){3}(25[0-5]|2[0-4]\d|((1\d{2})|([1-9]?\d)))$/; if(strRegex.test(ip)) //true: 通过 2. 三种弹窗方式: alert();confirm(); prompt() 1&a…...

【Android】重写onClick方法时,显示Method does not override method from its supperclass
问题 重写onClick方法时,显示Method does not override method from its supperclass 解决 在类上加implements View.OnClickListener...
LeetCode 第397场周赛个人题解
目录 100296. 两个字符串的排列差 原题链接 思路分析 AC代码 100274. 从魔法师身上吸取的最大能量 原题链接 思路分析 AC代码 100281. 矩阵中的最大得分 原题链接 思路分析 AC代码 100312. 找出分数最低的排列 原题链接 思路分析 AC代码 100296. 两个字符串的排…...

Mysql数据库二进制日志导致磁盘满了处理过程
数据库的二进制日志是数据库管理系统(DBMS)用来记录所有对数据库进行修改的操作的记录。这种日志对于数据库的备份、恢复、复制和审计等操作至关重要。 以MySQL数据库为例,二进制日志(Binary Log)记录了所有更改数据的…...
前端面试题日常练-day07 【面试题】
题目 希望这些选择题能够帮助您进行前端面试的准备,答案在文末。 1. 在 JavaScript 中,以下哪个方法可以用于从数组的末尾添加一个或多个元素? A) push() B) pop() C) shift() D) unshift()2. 下列哪个 HTML 标签用于定义表格的表头&#…...
Uniapp H5开发常见问题解析
引言 在移动应用开发领域,Uniapp已经成为一个备受瞩目的技术框架,其跨平台能力和高效开发特性使得开发者能够更加便捷地构建出功能丰富、性能优越的应用程序。特别是在H5开发中,Uniapp的应用场景日益广泛,然而,随之而…...

QT状态机4-使用并行状态来避免组合爆炸
#include "MainWindow.h" #include "ui_MainWindow.h"MainWindow::MainWindow(QWidget *parent):...
MemoryModule - 应用编程细节
文章目录 MemoryModule - 应用编程细节概述笔记实验环境升级MemoryModule,在上下文中加入DLL在内存载入前的信息MemoryModule.hMemoryModule.cpp实现接口MemoryGetPayload() 整理 - 在内存载入的DLL中,取得资源表中的信息,取得载入前的DLL内容…...
Java程序CPU持续高,如何排查?
首先找到进程ID jps然后找到该进程用占用cpu高的线程 top -Hp 进程ID将线程ID转化为十六进制 printf “0x%x” 线程ID使用jstack 工具跟踪堆栈定位问题 jstack 进程ID | grep 十六进制线程ID -A 5说明,最后-A 5是打印出来后5行。...
(Java)心得:LeetCode——15.三数之和
一、原题 给你一个整数数组 nums ,判断是否存在三元组 [nums[i], nums[j], nums[k]] 满足 i ! j、i ! k 且 j ! k ,同时还满足 nums[i] nums[j] nums[k] 0 。请你返回所有和为 0 且不重复的三元组。 注意:答案中不可以包含重复的三元组。…...
Rust中忽略JSON反序列化时的不必要字段
在Rust中处理JSON数据时,经常会遇到JSON数据中包含一些在目标数据结构中不存在的字段的情况。如果你使用的是serde和serde_json这两个流行的库来处理JSON,那么有一些简单的方法可以忽略这些不必要的字段。 默认行为:忽略未知字段 在Rust中&…...

UDP多对多组播通信
广播和多播仅应用于UDP。TCP是一个面向连接的协议,TCP一定是点对点的,一点是两个主机来建立连接的,TCP肯定是单播。只有UDP才会使用广播和组播。 如下示例实现一个UDP多对多的组播通信,进程中有收、发两个线程,分别表…...

Linux技术---部署PXE服务器实现批量安装操作系统
部署PXE服务器实现批量安装操作系统 部署PXE服务器实现批量安装操作系统 部署PXE服务器实现批量安装操作系统1.安装相关服务组件1.1 安装tftp和xinetd1.2 安装DHCP服务1.3 准备 Linux 内核、初始化镜像文件、 PXE 引导程序、安装FTP服务并准备安装源1.4 配置启动菜单文件1.5 验…...

日志:打印技巧
一、概览 Unity日志打印技巧 常规日志打印彩色日志日志存储与上传日志开关日志双击溯源 二、常规日志打印 1、打印Hello World 调用堆栈可以很好的帮助我们定位问题,特别是报错的Error日志 Debug.Log("Hello World");Debug.Log("This is a log m…...

二叉树的常见操作
建立树 复制二叉树 计算深度 计算总结点数 计算叶子结点数...

CSS 根据子元素选择父元素,并设置父元素的样式
场景举例:当子元素有增加了一个class时,需要影响其父元素的样式 可以使用":has"伪类来实现选择父元素的效果 <style>.parent:has(.child){background-color: #eee;}p{width:100px;border:1px solid #000;} </style> <body>…...
onnx转trt时,关于动态shape自动配置默认值的脚本
onnx转trt时,关于动态shape自动配置默认值,一般需要指定3个shape,分别是最小最优与最大。但是我们在测试时不想写那么多的代码,能否自动实现3个shape的配置,这里实现了一版。 import osimport tensorrt as trt import…...

实验室无法培养的菌,原来可以这么研究!
厌氧氨氧化(anammox)细菌在全球氮循环和废水氮去除中发挥着至关重要的作用,由于anammox细菌生长缓慢、难以培养等特点,对其生态学和生物学特性知之甚少。近日,凌恩生物合作客户重庆大学陈猷鹏教授团队在《Science of t…...

Xed编辑器开发第一期:使用Rust从0到1写一个文本编辑器
这是一个使用Rust实现的轻量化文本编辑器。学过Rust的都知道,Rust 从入门到实践中间还隔着好几个Go语言的难度,因此,如果你也正在学习Rust,那么恭喜你,这个项目被你捡到了。本项目内容较多,大概会分三期左右陆续发布&a…...

UE5 学习系列(二)用户操作界面及介绍
这篇博客是 UE5 学习系列博客的第二篇,在第一篇的基础上展开这篇内容。博客参考的 B 站视频资料和第一篇的链接如下: 【Note】:如果你已经完成安装等操作,可以只执行第一篇博客中 2. 新建一个空白游戏项目 章节操作,重…...

深度学习在微纳光子学中的应用
深度学习在微纳光子学中的主要应用方向 深度学习与微纳光子学的结合主要集中在以下几个方向: 逆向设计 通过神经网络快速预测微纳结构的光学响应,替代传统耗时的数值模拟方法。例如设计超表面、光子晶体等结构。 特征提取与优化 从复杂的光学数据中自…...

智慧工地云平台源码,基于微服务架构+Java+Spring Cloud +UniApp +MySql
智慧工地管理云平台系统,智慧工地全套源码,java版智慧工地源码,支持PC端、大屏端、移动端。 智慧工地聚焦建筑行业的市场需求,提供“平台网络终端”的整体解决方案,提供劳务管理、视频管理、智能监测、绿色施工、安全管…...
连锁超市冷库节能解决方案:如何实现超市降本增效
在连锁超市冷库运营中,高能耗、设备损耗快、人工管理低效等问题长期困扰企业。御控冷库节能解决方案通过智能控制化霜、按需化霜、实时监控、故障诊断、自动预警、远程控制开关六大核心技术,实现年省电费15%-60%,且不改动原有装备、安装快捷、…...
JVM垃圾回收机制全解析
Java虚拟机(JVM)中的垃圾收集器(Garbage Collector,简称GC)是用于自动管理内存的机制。它负责识别和清除不再被程序使用的对象,从而释放内存空间,避免内存泄漏和内存溢出等问题。垃圾收集器在Ja…...

定时器任务——若依源码分析
分析util包下面的工具类schedule utils: ScheduleUtils 是若依中用于与 Quartz 框架交互的工具类,封装了定时任务的 创建、更新、暂停、删除等核心逻辑。 createScheduleJob createScheduleJob 用于将任务注册到 Quartz,先构建任务的 JobD…...
linux 错误码总结
1,错误码的概念与作用 在Linux系统中,错误码是系统调用或库函数在执行失败时返回的特定数值,用于指示具体的错误类型。这些错误码通过全局变量errno来存储和传递,errno由操作系统维护,保存最近一次发生的错误信息。值得注意的是,errno的值在每次系统调用或函数调用失败时…...

QT: `long long` 类型转换为 `QString` 2025.6.5
在 Qt 中,将 long long 类型转换为 QString 可以通过以下两种常用方法实现: 方法 1:使用 QString::number() 直接调用 QString 的静态方法 number(),将数值转换为字符串: long long value 1234567890123456789LL; …...

华为云Flexus+DeepSeek征文|DeepSeek-V3/R1 商用服务开通全流程与本地部署搭建
华为云FlexusDeepSeek征文|DeepSeek-V3/R1 商用服务开通全流程与本地部署搭建 前言 如今大模型其性能出色,华为云 ModelArts Studio_MaaS大模型即服务平台华为云内置了大模型,能助力我们轻松驾驭 DeepSeek-V3/R1,本文中将分享如何…...

关键领域软件测试的突围之路:如何破解安全与效率的平衡难题
在数字化浪潮席卷全球的今天,软件系统已成为国家关键领域的核心战斗力。不同于普通商业软件,这些承载着国家安全使命的软件系统面临着前所未有的质量挑战——如何在确保绝对安全的前提下,实现高效测试与快速迭代?这一命题正考验着…...