HarmonyOS NEXT 闹钟表盘绘制方案分享

举报
哈莫尼OS 发表于 2025/03/16 12:07:44 2025/03/16
【摘要】 闹钟表盘绘制方案并且转动起来

【引言】

利用ArkUI的Canvas组件可以高效实现动态图形-闹钟表盘的绘制。本文将以实时时钟组件为例,展示如何通过Canvas的坐标变换与动画机制,实现高性能的动态图形渲染效果。

【实现效果】

GIF 2025-3-15 16-08-05.gif

【环境准备】

• 操作系统:Windows 10

• 开发工具:DevEco Studio(5.0.7.210)

• 目标设备:华为Mate60 Pro

• 开发语言:ArkTS

• 框架:ArkUI

• API版本:API 14


【工程目录】

├─ entry
│  ├─ obfuscation-rules.txt
│  ├─ oh-package.json5                     // 声明依赖
│  └─ src
│     ├─ main
│     │  ├─ ets
│     │  │  ├─ entryability
│     │  │  │  └─ EntryAbility.ets         //程序入口类
│     │  │  ├─ entrybackupability
│     │  │  │  └─ EntryBackupAbility.ets
│     │  │  ├─ images                      //图片资源类
│     │  │  │  ├─ ic_clock_pan.png
│     │  │  │  ├─ ic_confirm.png
│     │  │  │  ├─ ic_hour_pointer.png
│     │  │  │  ├─ ic_minute_pointer.png
│     │  │  │  ├─ ic_public_arrow_right.svg
│     │  │  │  └─ ic_second_pointer.png
│     │  │  └─ pages
│     │  │     └─ Index.ets                 //主页入口
│     │  ├─ module.json5                    // 模块配置相关
│     │  └─ resources                       // 应用静态资源
│     │     ├─ base
│     │     │  ├─ element
│     │     │  │  ├─ color.json
│     │     │  │  ├─ float.json
│     │     │  │  └─ string.json
│     │     │  ├─ media
│     │     │  │  ├─ background.png
│     │     │  │  ├─ foreground.png
│     │     │  │  ├─ layered_image.json
│     │     │  │  └─ startIcon.png
│     │     │  └─ profile
│     │     │     ├─ backup_config.json
│     │     │     └─ main_pages.json
│     │     ├─ dark
│     │     │  └─ element
│     │     │     └─ color.json
│     │     └─ rawfile

【具体实现】

1. 组件架构设计

@Entry
@Component
export struct CanvasComp {
  // 核心绘图对象配置
  private settings: RenderingContextSettings = new RenderingContextSettings(true)
  private context: CanvasRenderingContext2D = new CanvasRenderingContext2D(this.settings)
  
  // 资源预加载
  private panImage = new ImageBitmap("/images/ic_clock_pan.png")
  private hourImage = new ImageBitmap("/images/ic_hour_pointer.png")
  //...其他图片资源
}
  • 装饰器架构:采用@Entry@Component构建标准ArkUI组件
  • 绘图环境:通过RenderingContextSettings启用抗锯齿渲染
  • 资源管理:预加载所有静态图片资源,提升渲染性能

2. 核心绘制流程

startDraw() {
  this.drawClock()
  setInterval(() => {
    this.drawClock()
  }, 1000)
}

drawClock() {
  this.context.clearRect(0, 0, this.canvasSize, this.canvasSize)
  this.context.drawImage(this.panImage, 0, 0, this.canvasSize, this.canvasSize)
  //...时间获取与指针绘制
}
  • 定时刷新:通过setInterval建立秒级刷新机制
  • 分层绘制
    1. 清空画布(clearRect
    2. 绘制静态表盘(drawImage
    3. 动态绘制指针

3. 坐标变换机制

drawPointer(img: ImageBitmap, angle: number = 0) {
  this.context.save()
  this.context.translate(this.canvasSize/2, this.canvasSize/2)
  this.context.rotate((180 + angle) * Math.PI / 180)
  //...绘制逻辑
  this.context.restore()
}
  • 坐标系控制
    • translate()移动坐标系到画布中心
    • rotate()根据时间计算旋转角度(角度转弧度公式:* Math.PI/180
  • 状态堆栈save()/restore()保证每次绘制不影响全局状态

4. 界面构建

build() {
  Column() {//列容器组件
    Canvas(this.context)
      .aspectRatio(1)
      .onReady(() => {
        this.startDraw()
      })
  }
  .height('100%')//列容器高度
  .justifyContent(FlexAlign.Center)//表盘居中
}
  • 自适应布局:通过aspectRatio(1)保持正方形画布
  • 生命周期:利用onReady回调启动绘制流程
  • 居中布局:justifyContent属性实现主轴居中

完整代码:

@Entry
@Component
export struct CanvasComp {
  //抗锯齿设置
  private settings: RenderingContextSettings = new RenderingContextSettings(true)
  //创建画布实例context
  private context: CanvasRenderingContext2D = new CanvasRenderingContext2D(this.settings)
  //绘制图片,创建ImageBitmap对象 ,时针、分针、秒针
  private panImage = new ImageBitmap("/images/ic_clock_pan.png")
  private hourImage = new ImageBitmap("/images/ic_hour_pointer.png")
  private minuteImage = new ImageBitmap("/images/ic_minute_pointer.png")
  private secondImage = new ImageBitmap("/images/ic_second_pointer.png")
  //画布参数
  canvasSize = 252
  //时针、分针、秒针对应的宽度
  pointerWidth = 8

  //绘制图像:绘制表盘+设置定时
  startDraw() {
    //开始画图,表盘、时针、分针、秒针
    this.drawClock()
    setInterval(() => {
      this.drawClock()
    }, 1000)
  }

  //绘制表盘、时针、分针、秒针
  drawClock() {
    //清空先前的绘图
    this.context.clearRect(0, 0, this.canvasSize, this.canvasSize)
    //绘制表盘
    this.context.drawImage(this.panImage, 0, 0, this.canvasSize, this.canvasSize)

    //获取系统时间
    const date = new Date()
    const hour = date.getHours() + date.getMinutes() / 60 //获取系统当前小时
    const minute = date.getMinutes() //获取系统当前分钟
    const second = date.getSeconds() //获取系统当前秒

    //绘制时针
    this.drawPointer(this.hourImage, 360 * 1 / 12 * (hour % 12))

    //绘制分针
    this.drawPointer(this.minuteImage, 360 * 1 / 60 * minute)

    //绘制秒针
    this.drawPointer(this.secondImage, 360 * 1 / 60 * second)

  }

  //绘制表针
  drawPointer(img: ImageBitmap, angle: number = 0) {
    /**
     * 绘制表针
     */
    this.context.save() //保存
    //设置旋转顶点
    this.context.translate(this.canvasSize / 2, this.canvasSize / 2)

    //画布、图像旋转
    this.context.rotate((180 + angle) * Math.PI / 180)

    //设置旋转中心
    this.context.translate(-this.pointerWidth / 2, -this.canvasSize / 2)

    //绘制时针
    this.context.drawImage(img, 0, 0, this.pointerWidth, this.canvasSize)

    //设置旋转中心
    this.context.translate(this.canvasSize / 2, this.canvasSize / 2)

    //重置
    this.context.restore()

  }

  build() {
    Column() {
      Canvas(this.context)
        .width(this.canvasSize)
        .aspectRatio(1)// .backgroundColor('#ccc')
        .onReady(() => {
          this.startDraw()
        })
    }
    .height('100%')
    .width('100%')
    .justifyContent(FlexAlign.Center)
  }
}
图片资源:

ic_clock_pan.png

ic_hour_pointer.png

ic_minute_pointer.png

ic_second_pointer.png











【声明】本内容来自华为云开发者社区博主,不代表华为云及华为云开发者社区的观点和立场。转载时必须标注文章的来源(华为云社区)、文章链接、文章作者等基本信息,否则作者和本社区有权追究责任。如果您发现本社区中有涉嫌抄袭的内容,欢迎发送邮件进行举报,并提供相关证据,一经查实,本社区将立刻删除涉嫌侵权内容,举报邮箱: cloudbbs@huaweicloud.com
  • 点赞
  • 收藏
  • 关注作者

评论(0

0/1000
抱歉,系统识别当前为高风险访问,暂不支持该操作

全部回复

上滑加载中

设置昵称

在此一键设置昵称,即可参与社区互动!

*长度不超过10个汉字或20个英文字符,设置后3个月内不可修改。

*长度不超过10个汉字或20个英文字符,设置后3个月内不可修改。