时钟动效开发(OpenHarmony) 原创 精华

野生菌君
发布于 2023-6-21 16:45
浏览
1收藏

时钟开发

场景介绍

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

效果呈现

本例最终效果如下:

时钟动效开发(OpenHarmony)-鸿蒙开发者社区

运行环境

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

  • IDE: DevEco Studio 3.1 Beta2
  • SDK: Ohos_sdk_public 3.2.11.9 (API Version 9 Release)

实现思路

  • 表盘方式的展示:通过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(); //对保存的绘图上下文进行恢复
    ...
    
  2. 时钟方式的转换:通过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("red")
    .width('40%')
    .height('5%')
    .onClick(() => {
      router.back()
    })
    ...
    
  3. 数字时间方式:使用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')
  }
}

参考

©著作权归作者所有,如需转载,请注明出处,否则将追究法律责任
3
收藏 1
回复
举报
4条回复
按时间正序
/
按时间倒序
红叶亦知秋
红叶亦知秋

感觉非常适合做到手表上

回复
2023-6-21 17:31:21
麻辣香锅配馒头
麻辣香锅配馒头

学习下源码

回复
2023-6-25 10:47:50
野生菌君
野生菌君 回复了 红叶亦知秋
感觉非常适合做到手表上

是的,运动手表的表盘是比较适合的

回复
2023-6-30 12:23:34
野生菌君
野生菌君 回复了 麻辣香锅配馒头
学习下源码

一起学习!

回复
2023-6-30 12:23:51
回复
    相关推荐