GHOST系统之家 - Windows系统光盘下载网站!
当前位置:GHOST系统之家>系统教程 > OpenHarmony - 基于ArkUI(eTS)开发石英钟表自定义组件

OpenHarmony - 基于ArkUI(eTS)开发石英钟表自定义组件

来源:Ghost系统之家浏览:时间:2023-04-14 11:30:26

OpenHarmony - 基于ArkUI(eTS)开发石英钟表自定义组件

作者:余香鑫 2022-09-16 15:34:32系统 OpenHarmony Canvas是画布组件, 默认坐标原点在左上顶点. 构造函数接收一个CanvasRenderingContext2D对象, 可以理解为画笔Paint, 它提供了绘制矩形、文字、图片等API, 还支持对Canvas缩放。

​​想了解更多关于开源的内容,请访问:​​

​​51CTO开源基础软件社区​​

​​https://ost.51cto.com​​

前言

最近项目有用到Canvas组件,想扩展熟悉下eTS Canvas组件,便有了这个项目。先看下实现效果,左边是参考样例, 右边是最终实现效果

(原生字体看起来不太协调, 但没有找到换字体的方法)。

原型

实现效果

OpenHarmony - 基于ArkUI(eTS)开发石英钟表自定义组件-开源基础软件社区

OpenHarmony - 基于ArkUI(eTS)开发石英钟表自定义组件-开源基础软件社区

开发环境IDE: DevEco Studio 3.0 Beta4SDK: API9,3.2.5.5

准备工作

开始之前, 我们需要对Canvas有一些基础概念

Canvas是画布组件, 默认坐标原点在左上顶点. 构造函数接收一个CanvasRenderingContext2D​对象, 可以理解为画笔Paint, 它提供了绘制矩形、文字、图片等API, 还支持对Canvas缩放、旋转、平移等能力, 基于这些能力, 我们可以实现常规组件难以实现的效果。

OpenHarmony - 基于ArkUI(eTS)开发石英钟表自定义组件-开源基础软件社区

beginPath和closePath接口, 一个Canvas只能设置一个CanvasRenderingContext2D对象, 在绘制不同区域不同样式时为了不了会互相干扰。

绘制之前调用CanvasRenderingContext2D#beginPath()方法重置路径, 绘制结束调用 CanvasRenderingContext2D#closePath()方法结束绘制区间, 以下为示例:

// 绘制一个直角, 填充色为 Red canvas.beginPath() canvas.fillStyle = Color.Red.toString() // 起点为 50, 100; width=30, height=50 canvas.fillRect(50, 100, 30, 50) canvas.closePath() // 绘制一个0-90度的弧形, 填充色为 Yellow canvas.beginPath() canvas.fillStyle = Color.Yellow.toString() canvas.moveTo(100, 100) // 起点为 100, 100; 半径=50 canvas.arc(100, 100, 50, 0, toCanvasAngle(90)) canvas.fill() canvas.closePath()

OpenHarmony - 基于ArkUI(eTS)开发石英钟表自定义组件-开源基础软件社区

需求拆解: 参照原型图示, 按动静模型可以拆分为背景表盘和旋转的分秒指针. 背景表盘由由外框和时间刻度组成, 主要使用绘制弧形和文字接口, 时分秒指针需要根据时间计算出旋转角度, 对画布旋转对应角度后再绘制上即可。封装RectF: Canvas绘制时经常需要用到坐标点和大小, 我们可以定义一个class RectF, 维护表盘绘制区域的上下左右4个点的位置, 以便计算绘制区域大小和中心点。class RectF {public left: numberpublic top: numberpublic right: numberpublic bottom: numberconstructor(left: number, top: number, right: number, bottom: number) {this.left = leftthis.top = topthis.right = rightthis.bottom = bottom}width(): number{return this.right - this.left}height(): number{return this.bottom - this.top}centerX(): number{return (this.right + this.left) * 0.5}centerY(): number{return (this.top + this.bottom) * 0.5}}

开始绘制

1、绘制Canvas组件

在页面build函数下添加Canvas组件, 初始化CanvasRenderingContext2D对象, 确定绘制区域。

@Entry@Componentstruct Clocks {// 表盘绘制大小componentSize = 300// 表盘显示区域displayRect: RectF// 画笔canvas: CanvasRenderingContext2D = new CanvasRenderingContext2D(new RenderingContextSettings(true))aboutToAppear() {this.displayRect = new RectF(0, 0, this.componentSize, this.componentSize)}build() {Column() {Canvas(this.canvas).height(this.componentSize).width(this.componentSize).onReady(() => {let canvas = this.canvaslet displayRect = this.displayRectcanvas.fillStyle = '#ffe0ad5d'canvas.fillRect(0, 0, displayRect.width(), displayRect.height())})}.width('100%').height('100%').justifyContent(FlexAlign.Center)}

2、绘制表盘外圈和内圈

表盘外圈由一个深灰色#4C4C4E的外层和和黑色#252529内层组合, 使用绘制弧形再填充即可实现. 但还需要镂空中部使用canvas.globalCompositeOperation属性可以实现。

OpenHarmony - 基于ArkUI(eTS)开发石英钟表自定义组件-开源基础软件社区

drawDial(canvas: CanvasRenderingContext2D, displayRect: RectF, innerRect: RectF) {// 绘制外圈canvas.save()this.fillCircle(displayRect.centerX(), displayRect.centerY(), displayRect.radius(), '#4C4C4E')this.fillCircle(displayRect.centerX(), displayRect.centerY(), displayRect.radius() - this.outCircleWidth, '#252529')// 镂空中部canvas.globalCompositeOperation = 'destination-out'this.fillCircle(displayRect.centerX(), displayRect.centerY(), displayRect.radius() - this.outCircleWidth - this.outCircleInnerWidth)canvas.restore()// 外圈中线canvas.beginPath()canvas.strokeStyle = '#ff726f6f'canvas.arc(displayRect.centerX(), displayRect.centerY(), displayRect.radius() - this.outCircleWidth, 0, toCanvasAngle(360))canvas.arc(displayRect.centerX(), displayRect.centerY(), displayRect.radius() - this.outCircleWidth - 0.5, 0, toCanvasAngle(360))canvas.stroke()canvas.closePath()// 轮盘下方文字canvas.beginPath()canvas.fillStyle = Color.Black.toString()canvas.font = '20px sans-serif'let bottomText = 'Chinasoftinc'let bottomTextMetrics = canvas.measureText(bottomText)canvas.fillText(bottomText, innerRect.centerX() - bottomTextMetrics.width / 2, innerRect.centerY() + this.innerRadius - 12)canvas.closePath()// 上方品牌标识let vendorImgWidth = 45let vendorImgHeight = vendorImgWidth / 1.74canvas.drawImage(this.vendorImg, innerRect.centerX() - vendorImgWidth / 2, innerRect.centerY() - this.innerRadius + 7, vendorImgWidth, vendorImgHeight)// 内圆canvas.beginPath()canvas.lineWidth = 0.5canvas.strokeStyle = '#727478'canvas.arc(innerRect.centerX(), innerRect.centerY(), this.innerRadius, 0, toCanvasAngle(360))canvas.stroke()canvas.closePath()}

3、绘制时间刻度线和小时数

绘制时间刻度使用lineTo方法可以实现, 关键点在如何确定起点和钟点位置, 这里需要用到直角三角形边长和角度关系公式, 使用Math.con和sin函数可以计算出任意角度的坐标. 绘制文字也同理, 实现效果如下:

OpenHarmony - 基于ArkUI(eTS)开发石英钟表自定义组件-开源基础软件社区

OpenHarmony - 基于ArkUI(eTS)开发石英钟表自定义组件-开源基础软件社区

drawAxis(canvas: CanvasRenderingContext2D, displayRect: RectF, innerRect: RectF) {canvas.save()canvas.beginPath()// 刻度线总个数let axisCount = 12 * 5// 每个刻度线角度let eachAxisAngle = 360 / axisCountlet radius = innerRect.radius()canvas.strokeStyle = Color.Black.toString()canvas.font = '120px GarnetItalic.ttf'let axisLineRadius = radius - this.axisLineLengthlet axisFontRadius = axisLineRadius - this.axisFontMargincanvas.textBaseline = 'middle'for (let i = 0; i < axisCount; i++) {let ange = eachAxisAngle * i - (2 / 3 * 90)let x = radius * Math.cos(toCanvasAngle(ange))let y = radius * Math.sin(toCanvasAngle(ange))let xTo = axisLineRadius * Math.cos(toCanvasAngle(ange))let yTo = axisLineRadius * Math.sin(toCanvasAngle(ange))// 绘制刻度线canvas.beginPath()canvas.lineWidth = i % 5 == 0 ? this.axisLineAtHourWidth : 1canvas.moveTo(innerRect.centerX() + x, innerRect.centerY() + y)canvas.lineTo(innerRect.centerX() + xTo, innerRect.centerY() + yTo)canvas.stroke()canvas.closePath()// 绘制小时数// 绘制小时数if (i % 5 == 0) {canvas.fillStyle = Color.Black.toString()let fontX = axisFontRadius * Math.cos(toCanvasAngle(ange))let fontY = axisFontRadius * Math.sin(toCanvasAngle(ange))let text = String((i + 5) / 5)let textMetrics = canvas.measureText(text);canvas.fillText(text, innerRect.centerX() + fontX - textMetrics.width / 2, innerRect.centerY() + fontY)}}canvas.closePath()canvas.restore()}

4、绘制时分秒指针

指针可以使用Canvas提供的连线能力完成, 主要工作在计算各个点的位置, 难点不大。

OpenHarmony - 基于ArkUI(eTS)开发石英钟表自定义组件-开源基础软件社区

/** * 绘制小时指针*/drawHourPointer(canvas: Canvas2, innerRect: RectF) {canvas.canvas.lineJoin = 'round'let topPoint = new Point(innerRect.centerX() - 40 /**预览用**/, innerRect.centerY() - innerRect.radius() * 0.13)let bottomPoint = new Point(topPoint.x, topPoint.y + innerRect.radius() * 0.78)// 底部灰色区域canvas.save().beginPath().fillStyle('#A5A7A7').moveToPoint(topPoint).lineTo(topPoint.x + 6, innerRect.centerY()).lineTo(topPoint.x + 6, innerRect.centerY() + innerRect.radius() * 0.1).lineToPoint(bottomPoint).lineTo(topPoint.x - 6, innerRect.centerY() + innerRect.radius() * 0.1).lineTo(topPoint.x - 6, innerRect.centerY()).lineToPoint(topPoint).fill().closePath()// 黑色指针区域let innerTopPoint = new Point(topPoint.x, topPoint.y + innerRect.radius() * 0.19)canvas.fillStyle(Color.Black.toString()).beginPath().moveTo(innerTopPoint.x, innerTopPoint.y) // 顶部凹点.lineTo(innerTopPoint.x + 4.5, innerTopPoint.y - 4) // 右上.lineTo(innerTopPoint.x + 4.5, innerTopPoint.y + 4) // 右中.lineToPoint(bottomPoint).lineTo(innerTopPoint.x - 4.5, innerTopPoint.y + 4) // 右中.lineTo(innerTopPoint.x - 4.5, innerTopPoint.y - 4) // 右上.fill().closePath().restore()}/** * 绘制秒钟指针 */drawSecondPointer(canvas: Canvas2, innerRect: RectF) {canvas.canvas.lineWidth = 1// 秒钟顶部三角形let topPoint = new Point(innerRect.centerX() + 40 /**预览用**/, innerRect.centerY() - innerRect.radius() * 0.30)let topRightPoint = new Point(topPoint.x + 3, topPoint.y + 3)let topLeftPoint = new Point(topPoint.x - 3, topPoint.y + 3)let lineRightPoint = new Point(topRightPoint.x - 1.5, topRightPoint.y)let lineLeftPoint = new Point(topLeftPoint.x + 1.5, topLeftPoint.y)let bottomPoint = new Point(topPoint.x, innerRect.centerY() + innerRect.radius() * 0.75)canvas.save().beginPath().fillStyle(Color.Black.toString()).moveToPoint(topPoint).lineToPoint(topRightPoint).lineToPoint(lineRightPoint).lineToPoint(bottomPoint).lineToPoint(lineLeftPoint).lineToPoint(topLeftPoint).lineToPoint(topPoint).fill().closePath().restore()}/** * 绘制分钟指针 */drawMinutePointer(canvas: Canvas2, innerRect: RectF) {canvas.canvas.lineJoin = 'round'let topPoint = new Point(innerRect.centerX(), innerRect.centerY() - innerRect.radius() * 0.15)let bottomPoint = new Point(topPoint.x, topPoint.y + innerRect.radius() * 0.90)canvas.save().beginPath().fillStyle('#A5A7A7').fillStyle('#ff0bdbaa').beginPath().moveToPoint(topPoint).lineTo(topPoint.x + 5, innerRect.centerY()).lineTo(topPoint.x + 5, innerRect.centerY() + innerRect.radius() * 0.1).lineToPoint(bottomPoint).lineTo(topPoint.x - 5, innerRect.centerY() + innerRect.radius() * 0.1).lineTo(topPoint.x - 5, innerRect.centerY()).lineToPoint(topPoint).fill().closePath()let innerTopPoint = new Point(topPoint.x, topPoint.y + innerRect.radius() * 0.22)canvas.fillStyle(Color.Black.toString()).beginPath().moveTo(innerTopPoint.x, innerTopPoint.y) // 顶部凹点.lineTo(innerTopPoint.x + 4, innerTopPoint.y - 4) // 右上.lineTo(innerTopPoint.x + 4, innerTopPoint.y + 4) // 右中.lineToPoint(bottomPoint).lineTo(innerTopPoint.x - 4, innerTopPoint.y + 4) // 右中.lineTo(innerTopPoint.x - 4, innerTopPoint.y - 4) // 右上.fill().closePath().restore()}/** * 绘制指针中心点 */drawClockCenter(canvas: CanvasRenderingContext2D, innerRect: RectF) {let centerRadius = 4canvas.save()canvas.beginPath()canvas.shadowBlur = 2canvas.shadowColor = 'rgba(12, 12, 12, 1.00)'canvas.moveTo(innerRect.centerX(), innerRect.centerY())canvas.arc(innerRect.centerX(), innerRect.centerY(), centerRadius, toCanvasAngle(0), toCanvasAngle(360))canvas.fill()canvas.closePath()canvas.restore()for (let i = 0; i < 360; i += 90) {let startAngle = i + 90 / 2canvas.beginPath()canvas.fillStyle = i / 90 % 2 == 0 ? '#A9A8AD' : '#F3F3F7'canvas.moveTo(innerRect.centerX(), innerRect.centerY())canvas.arc(innerRect.centerX(), innerRect.centerY(), centerRadius, toCanvasAngle(startAngle), toCanvasAngle(startAngle + 90))canvas.fill()canvas.closePath()}}

5、时分秒指针联动

到现在我们绘制的主要工作均已完成, 还剩下最后一个工作, 启动一个定时器, 定时计算出时分秒各指针的旋转角度, 在绘制的指针前对画布做旋转操作即可。

OpenHarmony - 基于ArkUI(eTS)开发石英钟表自定义组件-开源基础软件社区

/** * 组件初始化时创建定时任务 */aboutToAppear(){...setInterval(() => {this.date = this.getDate()}, 1000)} /** * 计算时分秒 */getDate(): string{let current = new Date();this.currentHours = current.getHours()this.currentMinutes = current.getMinutes()this.currentSeconds = current.getSeconds()return `${complement(this.currentHours)}:${complement(this.currentMinutes)}:${complement(this.currentSeconds)}`}// 根据时间转换分针旋转角度this.currentMinutes / 60 * 360 + this.currentSeconds / 60 / 12 / 5 * 360// 根据时间转换时针旋转角度this.currentHours * 5 / 60 * 360 + this.currentMinutes / 60 / 12 * 360// 根据时间转换秒针旋转角度this.currentSeconds / 60 * 360/** * 绘制分钟指针 */drawMinutePointer(){canvas.save().beginPath().translate(innerRect.centerX(), innerRect.centerY()).rotate(toCanvasAngle(this.conventDateMinuteToAngle() + 180)).translate(-innerRect.centerX(), -innerRect.centerY())}...}

结束语

至此一块石英钟表组件已经完成,总体来说技术难点不大,主要使用Canvas绘制弧形和Path方法。

​​想了解更多关于开源的内容,请访问:​​

​​51CTO开源基础软件社区​​

​​https://ost.51cto.com​​。

责任编辑:jianghua 来源:​​51CTO开源基础软件社区 CanvasArkUI

推荐系统

  • 微软Win11原版22H2下载_Win11GHOST 免 激活密钥 22H2正式版64位免费下载

    微软Win11原版22H2下载_Win11GHOST 免 激活密钥 22H2正式版64位免费下载

    语言:中文版系统大小:5.13GB系统类型:Win11

    微软Win11原版22H2下载_Win11GHOST 免 激活密钥 22H2正式版64位免费下载系统在家用办公上跑分表现都是非常优秀,完美的兼容各种硬件和软件,运行环境安全可靠稳定。Win11 64位 Office办公版(免费)优化  1、保留 Edge浏览器。  2、隐藏“操作中心”托盘图标。  3、保留常用组件(微软商店,计算器,图片查看器等)。  5、关闭天气资讯。 

  • Win11 21H2 官方正式版下载_Win11 21H2最新系统免激活下载

    Win11 21H2 官方正式版下载_Win11 21H2最新系统免激活下载

    语言:中文版系统大小:4.75GB系统类型:Win11

    Ghost Win11 21H2是微软在系统方面技术积累雄厚深耕多年,Ghost Win11 21H2系统在家用办公上跑分表现都是非常优秀,完美的兼容各种硬件和软件,运行环境安全可靠稳定。Ghost Win11 21H2是微软最新发布的KB5019961补丁升级而来的最新版的21H2系统,以Windows 11 21H2 22000 1219 专业版为基础进行优化,保持原汁原味,系统流畅稳定,保留常用组件

  • windows11中文版镜像 微软win11正式版简体中文GHOST ISO镜像64位系统下载

    windows11中文版镜像 微软win11正式版简体中文GHOST ISO镜像64位系统下载

    语言:中文版系统大小:5.31GB系统类型:Win11

    windows11中文版镜像 微软win11正式版简体中文GHOST ISO镜像64位系统下载,微软win11发布快大半年了,其中做了很多次补丁和修复一些BUG,比之前的版本有一些功能上的调整,目前已经升级到最新版本的镜像系统,并且优化了自动激活,永久使用。windows11中文版镜像国内镜像下载地址微软windows11正式版镜像 介绍:1、对函数算法进行了一定程度的简化和优化

  • 微软windows11正式版GHOST ISO镜像 win11下载 国内最新版渠道下载

    微软windows11正式版GHOST ISO镜像 win11下载 国内最新版渠道下载

    语言:中文版系统大小:5.31GB系统类型:Win11

    微软windows11正式版GHOST ISO镜像 win11下载 国内最新版渠道下载,微软2022年正式推出了win11系统,很多人迫不及待的要体验,本站提供了最新版的微软Windows11正式版系统下载,微软windows11正式版镜像 是一款功能超级强大的装机系统,是微软方面全新推出的装机系统,这款系统可以通过pe直接的完成安装,对此系统感兴趣,想要使用的用户们就快来下载

  • 微软windows11系统下载 微软原版 Ghost win11 X64 正式版ISO镜像文件

    微软windows11系统下载 微软原版 Ghost win11 X64 正式版ISO镜像文件

    语言:中文版系统大小:0MB系统类型:Win11

    微软Ghost win11 正式版镜像文件是一款由微软方面推出的优秀全新装机系统,这款系统的新功能非常多,用户们能够在这里体验到最富有人性化的设计等,且全新的柔软界面,看起来非常的舒服~微软Ghost win11 正式版镜像文件介绍:1、与各种硬件设备兼容。 更好地完成用户安装并有效地使用。2、稳定使用蓝屏,系统不再兼容,更能享受无缝的系统服务。3、为

  • 雨林木风Windows11专业版 Ghost Win11官方正式版 (22H2) 系统下载

    雨林木风Windows11专业版 Ghost Win11官方正式版 (22H2) 系统下载

    语言:中文版系统大小:4.75GB系统类型:

    雨林木风Windows11专业版 Ghost Win11官方正式版 (22H2) 系统下载在系统方面技术积累雄厚深耕多年,打造了国内重装系统行业的雨林木风品牌,其系统口碑得到许多人认可,积累了广大的用户群体,雨林木风是一款稳定流畅的系统,一直以来都以用户为中心,是由雨林木风团队推出的Windows11国内镜像版,基于国内用户的习惯,做了系统性能的优化,采用了新的系统

  • 雨林木风win7旗舰版系统下载 win7 32位旗舰版 GHOST 免激活镜像ISO

    雨林木风win7旗舰版系统下载 win7 32位旗舰版 GHOST 免激活镜像ISO

    语言:中文版系统大小:5.91GB系统类型:Win7

    雨林木风win7旗舰版系统下载 win7 32位旗舰版 GHOST 免激活镜像ISO在系统方面技术积累雄厚深耕多年,加固了系统安全策略,雨林木风win7旗舰版系统在家用办公上跑分表现都是非常优秀,完美的兼容各种硬件和软件,运行环境安全可靠稳定。win7 32位旗舰装机版 v2019 05能够帮助用户们进行系统的一键安装、快速装机等,系统中的内容全面,能够为广大用户

  • 番茄花园Ghost Win7 x64 SP1稳定装机版2022年7月(64位) 高速下载

    番茄花园Ghost Win7 x64 SP1稳定装机版2022年7月(64位) 高速下载

    语言:中文版系统大小:3.91GB系统类型:Win7

    欢迎使用 番茄花园 Ghost Win7 x64 SP1 2022.07 极速装机版 专业装机版具有更安全、更稳定、更人性化等特点。集成最常用的装机软件,集成最全面的硬件驱动,精心挑选的系统维护工具,加上独有人性化的设计。是电脑城、个人、公司快速装机之首选!拥有此系统