(以下内容来自开发者分享,不代表 OpenHarmony 项目群工作委员会观点)

江英杰

华为技术有限公司

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

一、canvas 介绍

1.1 什么是 canvas?

在 Web 浏览器中,canvas 是一个可自定义 width、height 的矩形画布,画布左上角为坐标原点,以像素为单位,水平向右为 x 轴,垂直向下为 y 轴,画布内所有元素都基于原点进行定位。如图 1 所示,我们通过 <canvas> 标签,创建了一个 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() {
//创建canvas
Canvas(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;

注:本示例引用了部分开源资源,感兴趣的开发者可参考此开源资源,结合文中的实现思路补全代码。(https://github.com/xs528/game

以上就是本期全部内容,期待广大开发者能通过 canvas 组件绘制出精美的图形,更多 canvas 组件的详细使用方法,请参考文档进行学习:

https://developer.harmonyos.com/cn/docs/documentation/doc-references/js-components-canvas-canvas-0000000000621808

OpenHarmony 3.1 Beta 版本关键特性解析——ArkUI canvas组件的更多相关文章

  1. OpenHarmony 3.1 Beta版本关键特性解析——ArkUI容器类API介绍

    (以下内容来自开发者分享,不代表 OpenHarmony 项目群工作委员会观点) 刘鑫 容器类,顾名思义就是存储的类,用于存储各种数据类型的元素,并具备一系列处理数据元素的方法.在 ArkUI 开发框 ...

  2. OpenHarmony 3.1 Beta版本关键特性解析——HiStreamer框架大揭秘

    ​(以下内容来自开发者分享,不代表 OpenHarmony 项目群工作委员会观点)​ 陈国栋 数字多媒体技术在过去的数十年里得到了飞速的发展,多媒体终端设备如智能音箱.智能门锁.智能手表广泛应用于人们 ...

  3. OpenHarmony 3.1 Beta版本关键特性解析——HAP包安装实现剖析

    ​(以下内容来自开发者分享,不代表 OpenHarmony 项目群工作委员会观点)​ 石磊 随着社会的不断发展,人们逐渐注重更加高效.舒适.便捷.有趣的生活和工作体验. OpenAtom OpenHa ...

  4. OpenHarmony 3.1 Beta版本关键特性解析——探秘隐式查询

    ​(以下内容来自开发者分享,不代表 OpenHarmony 项目群工作委员会观点)​ 徐浩 隐式查询是 OpenAtom OpenHarmony(以下简称"OpenHarmony" ...

  5. OpenHarmony 3.1 Beta版本关键特性解析——OpenHarmony图形框架

    (以下内容来自开发者分享,不代表 OpenHarmony 项目群工作委员会观点) 李煜 华为技术有限公司 崔坤华为技术有限公司 众所周知,动画是系统和应用与用户交互的重要环节.动画效果的好坏会直接影响 ...

  6. OpenHarmony 3.1 Beta版本关键特性解析——分布式DeviceProfile

    (以下内容来自开发者分享,不代表 OpenHarmony 项目群工作委员会观点) 成翔 OpenAtom OpenHarmony(以下简称"OpenHarmony")作为分布式操作 ...

  7. OpenHarmony3.1 Release版本关键特性解析——Enhanced SWAP内存管理

    樊成阳 华为技术有限公司内核专家 陈杰 华为技术有限公司内核专家 OpenAtom OpenHarmony(以下简称"OpenHarmony")是面向全场景泛终端设备的操作系统,终 ...

  8. Unity 5.6 beta版本新特性

    http://manew.com/thread-98549-1-1.html 最新发布的beta版改进了编辑器和2D功能,图形性能更佳,加入新的视频播放器,并添加了对Facebook Gameroom ...

  9. OpenHarmony 3.1 Release版本发布

    OpenHarmony 3.1 Release 版本概述 当前版本在OpenHarmony 3.1 Beta的基础上,更新支持以下能力: 标准系统基础能力增强 本地基础音视频播放能力.视频硬编解码.相 ...

随机推荐

  1. 揭秘.NET Core剪裁器背后的技术

    十天前,我发布了对.NET Core程序进行瘦身的开源软件Zack.DotNetTrimmer,与.NET Core内置的剪裁器相比,Zack.DotNetTrimmer不仅对程序的剪裁效果更好,而且 ...

  2. java-Dos

    打开CMD的方式 1.菜单打开 2.Windows+R 输入cmd 3.shift+鼠标右键 选择在此处打开命令行窗口 4.资源管理器地址栏前+cmd 空格 管理员身份运行 常用的Dos命令 #盘符切 ...

  3. CF914G Sum the Fibonacci(FWT,FST)

    CF914G Sum the Fibonacci(FWT,FST) Luogu 题解时间 一堆FWT和FST缝合而来的丑陋产物. 对 $ cnt[s_{a}] $ 和 $ cnt[s_{b}] $ 求 ...

  4. 羽夏看Win系统内核——异常篇

    写在前面   此系列是本人一个字一个字码出来的,包括示例和实验截图.由于系统内核的复杂性,故可能有错误或者不全面的地方,如有错误,欢迎批评指正,本教程将会长期更新. 如有好的建议,欢迎反馈.码字不易, ...

  5. BUU findkey

    定位关键函数 跟入flag找到问题位置 两行一样的代码,nop掉第二行,按p生成函数 代码审计 int __userpurge sub_4018C4@<eax>(int a1@<eb ...

  6. 学习廖雪峰的Git教程3--从远程库克隆以及分支管理

    一.远程库克隆 这个就比较简单了, git clone git@github.com:****/Cyber-security.git 远程库的地址可以在仓库里一个clone or download的绿 ...

  7. 为什么redis 需要把所有数据放到内存中?

    答:Redis 为了达到最快的读写速度将数据都读到内存中,并通过异步的方式将数 据写入磁盘.所以 redis 具有快速和数据持久化的特征.如果不将数据放在内存中, 磁盘 I/O 速度为严重影响 red ...

  8. Linux上如何设置nginx开机启动

    连接上linux后输入以下命令--vim /etc/init.d/nginx 然后在这个空文件写入下面内容: 保存好后,修改下该文件权限--chmod 777 /etc/init.d/nginx 然后 ...

  9. 面试问题之C++语言:Overload、Override及Overwirte的区别

    Overload(重载):在C++程序中,可以将语义.功能相似的几个函数用同一个名字表示,但参数或返回值不同(包括类型.顺序不同),即函数重载. 特点: (1)相同的范围(在同一个类中) (2)函数名 ...

  10. Linux 环境下如何查找哪个线程使用 CPU 最长?

    1.获取项目的 pid,jps 或者 ps -ef | grep java,这个前面有讲过 2.top -H -p pid,顺序不能改变