HarmonyOS 鸿蒙Next开发案例 | 实现表盘时钟、数字时钟及切换

HarmonyOS 鸿蒙Next开发案例 | 实现表盘时钟、数字时钟及切换 场景介绍

常见的时钟呈现方式有两种,一种是表盘方式,一种是数字方式。用户可根据个人喜好在两种形式间进行切换。本例即为大家讲解如何开发上述两种钟表样式,以供参考。

效果呈现

本例最终效果如下:

最终效果动图

运行环境

本例基于以下环境开发,开发者也可以基于其他适配的版本进行开发:

  • IDE: DevEco Studio 3.1.1 Release
  • SDK: 3.1.0(API 9)

实现思路

  • 表盘方式的展示:通过Canvas组件提供画布;在画布上,通过CanvasRenderingContext2D对象使用RenderingContext在Canvas组件上进行绘制,绘制表盘上的数字、时针、分针、秒针。表盘上数字的分布使用fillText绘制填充类文本并确定其在画布上位置;表盘上时针的运动通过theta的角度决定时针的移动;分针和秒针同上。
  • 数字时间方式的展示:使用TextClock组件通过文本将系统时间显示在设备上。

开发步骤

根据上述思路,具体实现步骤如下:

  1. 表盘方式:通过CanvasRenderingContext2D对象使用RenderingContext在Canvas组件上进行绘制,绘制表盘上的数字、时针、分针、秒针。 首先,创建画布,具体代码如下:
// clock ets
clear() { // clear canvas function
  this.ctx.clearRect(0, 0, 360, 500);
}
drawScene() { // main drawScene function 绘制场景
  this.clear(); // clear canvas
...
build() {
  Column({ space: 5 }) {
    Canvas(this.ctx)
      .width(360)
      .height(500)
      .border({ width: 1, color: '#ffff00'})
      .onReady(() => {
        setInterval(() => {
          this.drawScene()
        }, 1000)
      })
      ...
  }
} 

声明相关变量,具体代码如下:

// clock ets
let date = new Date();
let hours = date.getHours();
let minutes = date.getMinutes();
let seconds = date.getSeconds();
hours = hours > 12 ? hours - 12 : hours;
let hour = hours + minutes / 60;
let minute = minutes + seconds / 60;

使用fillText方法绘制表盘数字并确定其位置

// clock ets
...
// draw numbers
this.ctx.font = '36px Arial'; //文本尺寸
this.ctx.fillStyle = '#000';   //指定绘制的填充色
this.ctx.textAlign = 'center'; // 文本对齐
this.ctx.textBaseline = 'middle'; //文本基线
for (let n = 0; n < 12; n++) {
  let theta = (n - 2) * (Math.PI * 2) / 12;
  let x = clockRadius * 0.7 * Math.cos(theta);
  let y = clockRadius * 0.7 * Math.sin(theta);
  this.ctx.fillText(`${n + 1}`, x, y);  // 表盘数字所在的位置
...

时针的移动路径,具体代码如下:

// clock ets
...
// draw hour
this.ctx.save(); //将当前状态放入栈中,保存canvas的全部状态,通常在需要保存绘制状态时调用
let theta = (hour - 3) * 2 * Math.PI / 12;
this.ctx.rotate(theta); //顺时针旋转
this.ctx.beginPath(); //创建一个新的绘制路径
this.ctx.moveTo(-15, -5); //绘制时针组件 起始点
this.ctx.lineTo(-15, 5);
this.ctx.lineTo(clockRadius * 0.3, 1);
this.ctx.lineTo(clockRadius * 0.3, -1); //绘制时针组件 终点
this.ctx.fillStyle = 'green';
this.ctx.fill();
this.ctx.restore(); //对保存的绘图上下文进行恢复
...

分针的移动路径,具体代码如下:

// clock ets
...
// draw minute
this.ctx.save();
theta = (minute - 15) * 2 * Math.PI / 60;
this.ctx.rotate(theta); //顺时针旋转
this.ctx.beginPath(); //创建一个新的绘制路径
this.ctx.moveTo(-15, -4); //绘制分针组件 起始点
this.ctx.lineTo(-15, 4);
this.ctx.lineTo(clockRadius * 0.45, 1);
this.ctx.lineTo(clockRadius * 0.45, -1); //绘制分针组件 终点
this.ctx.fillStyle = 'red';
this.ctx.fill();
this.ctx.restore(); //对保存的绘图上下文进行恢复
...

秒针的移动路径,具体代码如下:

// clock ets
...
// draw second
this.ctx.save();
theta = (seconds - 15) * 2 * Math.PI / 60;
this.ctx.rotate(theta); //顺时针旋转
this.ctx.beginPath(); //创建一个新的绘制路径
this.ctx.moveTo(-15, -3); //绘制秒针组件 起始点
this.ctx.lineTo(-15, 3);
this.ctx.lineTo(clockRadius * 0.6, 1);
this.ctx.lineTo(clockRadius * 0.6, -1); //绘制秒针组件 终点
this.ctx.fillStyle = 'black';
this.ctx.fill();
this.ctx.restore(); //对保存的绘图上下文进行恢复
...
  1. 时钟方式的转换:通过Button组件中的onClick事件进行切换页面。 从表盘方式往数字方式转换,具体代码如下:
// clock.ets
...
Button(){
  Text("切换")
    .fontSize(30)
    .fontWeight(FontWeight.Regular)
}
.type(ButtonType.Capsule)
.margin({top:20
})
.backgroundColor("red")
.width('40%')
.height('5%')
.onClick(()=>{
  router.pushUrl({url:'pages/Index1'})
})
...

从数字时间方式往表盘方式转换,具体代码如下:

// TextClock.ets
...
Button() {
  Text("切换")
    .fontSize(30)
    .fontWeight(FontWeight.Regular)
}
.type(ButtonType.Capsule)
.margin({ top: 20
})
.backgroundColor("#E8A027")
.width('40%')
.height('5%')
.onClick(() => {
  router.back()
})
...
  1. 数字时间方式:使用TextClock组件通过文本将当前系统时间显示在设备上。 具体代码如下:
// TextClock.ets
import router from '@ohos.router'
@Entry
@Component
struct Second {
  @State accumulateTime: number = 0
  // 导入对象
  controller: TextClockController = new TextClockController()

  build() {
    Flex({ direction: FlexDirection.Column, alignItems: ItemAlign.Center, justifyContent: FlexAlign.Center }) {
      TextClock({ timeZoneOffset: -8, controller: this.controller })
        .format('hms') //数字时间格式
        .onDateChange((value: number) => {
          this.accumulateTime = value
        })
        .margin(20)
        .fontSize(30)
        ...
    }
  }
}

完整代码

完整示例代码如下: 表盘时钟代码页

// clock.ets
import router from '@ohos.router';
const clockRadius = 180;

@Entry
@Component
struct Test10 {
 private settings: RenderingContextSettings = new RenderingContextSettings(true);
 private ctx: CanvasRenderingContext2D = new CanvasRenderingContext2D(this.settings);

 // 绘制函数
 clear() {
   this.ctx.clearRect(0, 0, 360, 500);
 }
 drawScene() { // 绘制场景
   this.clear(); // 清空画布
   // 获取当前时间
   let date = new Date();
   let hours = date.getHours();
   let minutes = date.getMinutes();
   let seconds = date.getSeconds();
   hours = hours > 12 ? hours - 12 : hours;
   let hour = hours + minutes / 60;
   let minute = minutes + seconds / 60;
   this.ctx.save();
   this.ctx.translate(360 / 2, 500 / 2);
   this.ctx.beginPath(); //创建一个新的绘制路径

   // 绘制表盘数字
   this.ctx.font = '45px Arial'; //文本尺寸
   this.ctx.fillStyle = '#000';   //指定绘制的填充色
   this.ctx.textAlign = 'center'; // 文本对齐
   this.ctx.textBaseline = 'middle'; //文本基线
   for (let n = 0; n < 12; n++) {
     let theta = (n - 2) * (Math.PI * 2) / 12;
     let x = clockRadius * 0.7 * Math.cos(theta);
     let y = clockRadius * 0.7 * Math.sin(theta);
     this.ctx.fillText(`${n + 1}`, x, y);  // 表盘数字所在的位置
   }

   // 绘制时针
   this.ctx.save(); //将当前状态放入栈中,保存canvas的全部状态,通常在需要保存绘制状态时调用
   let theta = (hour - 3) * 2 * Math.PI / 12;
   this.ctx.rotate(theta); //顺时针旋转
   this.ctx.beginPath(); //创建一个新的绘制路径
   this.ctx.moveTo(-15, -5); //绘制时针组件 起始点
   this.ctx.lineTo(-15, 5);
   this.ctx.lineTo(clockRadius * 0.3, 1);
   this.ctx.lineTo(clockRadius * 0.3, -1);
   this.ctx.fillStyle = 'green';
   this.ctx.fill();
   this.ctx.restore(); //对保存的绘图上下文进行恢复

   // 绘制分针
   this.ctx.save();
   theta = (minute - 15) * 2 * Math.PI / 60;
   this.ctx.rotate(theta); //顺时针旋转
   this.ctx.beginPath(); //创建一个新的绘制路径
   this.ctx.moveTo(-15, -4); //绘制分针组件 起始点
   this.ctx.lineTo(-15, 4);
   this.ctx.lineTo(clockRadius * 0.45, 1);
   this.ctx.lineTo(clockRadius * 0.45, -1);
   this.ctx.fillStyle = 'red';
   this.ctx.fill();
   this.ctx.restore(); //对保存的绘图上下文进行恢复

   // 绘制秒针
   this.ctx.save();
   theta = (seconds - 15) * 2 * Math.PI / 60;
   this.ctx.rotate(theta); //顺时针旋转
   this.ctx.beginPath(); //创建一个新的绘制路径
   this.ctx.moveTo(-15, -3); //绘制秒针组件 起始点
   this.ctx.lineTo(-15, 3);
   this.ctx.lineTo(clockRadius * 0.6, 1);
   this.ctx.lineTo(clockRadius * 0.6, -1);
   this.ctx.fillStyle = 'black';
   this.ctx.fill();
   this.ctx.restore(); //对保存的绘图上下文进行恢复

   this.ctx.restore(); //对保存的绘图上下文进行恢复
 }

 build() {
   Column({ space: 5 }) {
     Canvas(this.ctx)
       .width(360)
       .height(500)
       .onReady(() => {
         setInterval(() => {
           this.drawScene()
         }, 1000)
       })
     Button(){
       Text("切换")
         .fontSize(30)
         .fontWeight(FontWeight.Regular)
     }
     .type(ButtonType.Capsule)
     .margin({top:20
     })
     .backgroundColor('#E8A027')
     .width('40%')
     .height('5%')
     .onClick(()=>{
       router.pushUrl({url:'pages/TextClock'})
     })
   }.width('100%')
   .height('100%')
   .backgroundColor('#A4AE75')
 }
}

数字时间代码页:

//TextClock.ets
import router from '@ohos.router'
@Entry
@Component
struct Second {
 @State accumulateTime: number = 0
 // 导入对象
 controller: TextClockController = new TextClockController()

 build() {
   Flex({ direction: FlexDirection.Column, alignItems: ItemAlign.Center, justifyContent: FlexAlign.Center }) {

     TextClock({ timeZoneOffset: -8, controller: this.controller }) //timeZoneOffset 时区偏移ian
       .format('hms')
       .onDateChange((value: number) => {
         this.accumulateTime = value
       })
       .margin(20)
       .fontSize(30)
     Button() {
       Text("切换")
         .fontSize(30)
         .fontWeight(FontWeight.Regular)
     }
     .type(ButtonType.Capsule)
     .margin({ top: 20
     })
     .backgroundColor('#E8A027')
     .width('40%')
     .height('5%')
     .onClick(() => {
       router.back()
     })
   }
   .width('100%')
   .height('100%')
   .backgroundColor('#D4C3B3')
 }
}

参考

Canvas

CanvasRenderingContext2D对象

TextClock


更多关于HarmonyOS 鸿蒙Next开发案例 | 实现表盘时钟、数字时钟及切换的实战教程也可以访问 https://www.itying.com/category-93-b0.html

1 回复

更多关于HarmonyOS 鸿蒙Next开发案例 | 实现表盘时钟、数字时钟及切换的实战系列教程也可以访问 https://www.itying.com/category-93-b0.html


姓名: John Doe
职位: 软件工程师
简介: 拥有超过10年的软件开发经验,专注于Web应用和移动应用开发。在多个行业成功交付项目,包括金融、医疗保健和零售。

回到顶部