首页 > 其他分享 >鸿蒙自定义控件实现罗盘数字时钟效果

鸿蒙自定义控件实现罗盘数字时钟效果

时间:2024-03-23 13:59:01浏览次数:17  
标签:控件 rotate 自定义 鸿蒙 index canvasWidth context PI Math

前言:

DevEco Studio版本:4.0.0.600

关注过我的小伙伴一定知道我之前写过一篇基于Android的 仿抖音效果的数字时钟罗盘 最近看了鸿蒙的Canvas组件,今天通过Canvas组件也实现下罗盘数字时钟的效果。

参考链接:OpenHarmony Canvas   OpenHarmony Canvasrenderingcontext2d

效果:

实现原理分析

之前安卓实现是通过matrix矩阵的方式实现,但是在鸿蒙(API10)中暂时没有给画笔设置矩阵的方法。通过查找CanvasRenderingContext2D的方法,发现rotate方法比较符合要求。

已知圆形的整个弧度为:2 * Math.PI,那么每一秒的弧度为:2 * Math.PI / 60 * (每一秒所占弧度的比例)

通过for循环可得到每一秒的弧度需要旋转的弧度为:

rotate(2 * Math.PI / 60 * (index + 1 ) //index数组角标

为了使时分秒保持在右侧水平线上,就只能通过旋转坐标系来保持当前时间所在X轴坐标系一直在原始0或60秒的那个坐标系上,那么旋转的弧度为:2 * Math.PI / 60 * (每一秒所占弧度的比例 - 当前秒值所占弧度的比例)

通过for循环可得到每一秒的弧度在原始X轴方向为:

rotate(2 * Math.PI / 60 * (index + 1 - second))//index数组角标

同理可得分钟的每一分的弧度在原始X轴方向为:

rotate(2 * Math.PI / 60 * (index + 1 - minute))

时钟的每一时的弧度在原始X轴方向为:

rotate(2 * Math.PI / 12 * (index + 1 - hour))

代码实现:

1、Canvas创建

private settings: RenderingContextSettings = new RenderingContextSettings(true)
private context: CanvasRenderingContext2D = new CanvasRenderingContext2D(this.settings)
@State canvasWidth: number = 500 // 500是表盘默认大小
private radius: number = 40 // 默认表盘半径

build() {
   Stack({ alignContent: Alignment.Center }) {
      Canvas(this.context)
         .padding({ top: 76 })
         .width(this.canvasWidth)
         .height(this.canvasWidth + HEIGHT_ADD)
         .onReady(() => {
            
         })
   }
   .width('100%')
   .height('100%')
   .backgroundColor(Color.Black)
}

2、定时器每隔一秒获取时间

Canvas(this.context)
   .padding({ top: 76 })
   .width(this.canvasWidth)
   .height(this.canvasWidth + HEIGHT_ADD)
   .onReady(() => {
      //添加setInterval定时器,每隔1s执行一次updateTime函数
      this.intervalId = setInterval(this.updateTime, 1000)
   })


updateTime = () => {
   this.context.clearRect(0, 0, this.canvasWidth, this.canvasWidth + HEIGHT_ADD)
   let nowTime = new Date()
   let hour = nowTime.getHours()
   let minute = nowTime.getMinutes()
   let second = nowTime.getSeconds()
   console.info("22222222222   hour: " + hour + "  minute: " + minute + "  second: " + second)

   this.context.translate(this.canvasWidth / 2, this.canvasWidth / 2) //移动当前坐标系的原点
   this.drawTime(hour, minute, second)
   this.context.translate(-this.canvasWidth / 2, -this.canvasWidth / 2) //当前坐标系的原点复原
}

3、数据初始化

const hours: Array<string> = ["一点", "二点", "三点", "四点", "五点", "六点", "七点", "八点", "九点", "十点", "十一点", "十二点"];

const minutes: Array<string> = ["一分", "二分", "三分", "四分", "五分", "六分", "七分", "八分", "九分", "十分", "十一分", "十二分", "十三分", "十四分", "十五分", "十六分", "十七分", "十八分", "十九分",
   "二十分", "二十一分", "二十二分", "二十三分", "二十四分", "二十五分", "二十六分", "二十七分", "二十八分", "二十九分", "三十分", "三十一分", "三十二分", "三十三分", "三十四分", "三十五分", "三十六分",
   "三十七分", "三十八分", "三十九分", "四十分", "四十一分", "四十二分", "四十三分", "四十四分", "四十五分", "四十六分", "四十七分", "四十八分", "四十九分", "五十分", "五十一分", "五十二分", "五十三分",
   "五十四分", "五十五分", "五十六分", "五十七分", "五十八分", "五十九分", ""];

const seconds: Array<string> = ["一秒", "二秒", "三秒", "四秒", "五秒", "六秒", "七秒", "八秒", "九秒", "十秒", "十一秒", "十二秒", "十三秒", "十四秒", "十五秒", "十六秒", "十七秒", "十八秒", "十九秒",
   "二十秒", "二十一秒", "二十二秒", "二十三秒", "二十四秒", "二十五秒", "二十六秒", "二十七秒", "二十八秒", "二十九秒", "三十秒", "三十一秒", "三十二秒", "三十三秒", "三十四秒", "三十五秒", "三十六秒",
   "三十七秒", "三十八秒", "三十九秒", "四十秒", "四十一秒", "四十二秒", "四十三秒", "四十四秒", "四十五秒", "四十六秒", "四十七秒", "四十八秒", "四十九秒", "五十秒", "五十一秒", "五十二秒", "五十三秒",
   "五十四秒", "五十五秒", "五十六秒", "五十七秒", "五十八秒", "五十九秒", ""];

4、绘制时分秒

drawTime(hour: number, minute: number, second: number) {
   this.context.save()

   hours.forEach((value, index) => {
      this.context.fillStyle = (hour % 12) == (index + 1) ? '#0080DC' : '#FFFFFF'
      this.context.font = (hour % 12) == (index + 1) ? '16px' : "14px"
      this.context.textAlign = "start"
      this.context.textBaseline = "middle"

      this.context.rotate(2 * Math.PI / 12 * (index + 1 - hour))
      this.context.fillText(value, this.radius + 30, 0)
      this.context.rotate(-2 * Math.PI / 12 * (index + 1 - hour))
      this.context.save()
      this.context.restore()
   })

   minutes.forEach((value, index) => {
      this.context.fillStyle = minute == (index + 1) ? '#0080DC' : '#FFFFFF'
      this.context.font = minute == (index + 1) ? '16px' : "14px"
      this.context.textAlign = "start"
      this.context.textBaseline = "middle"
      this.context.rotate(2 * Math.PI / 60 * (index + 1 - minute))
      this.context.fillText(value, this.radius + 70, 0)
      this.context.rotate(-2 * Math.PI / 60 * (index + 1 - minute))
      this.context.save()
      this.context.restore()
   })

   seconds.forEach((value, index) => {
      this.context.fillStyle = second == (index + 1) ? '#0080DC' : '#FFFFFF'
      this.context.font = second == (index + 1) ? '16px' : "14px"
      this.context.textAlign = "start"
      this.context.textBaseline = "middle"
      this.context.rotate(2 * Math.PI / 60 * (index + 1 - second))
      this.context.fillText(value, this.radius + 120, 0)
      this.context.rotate(-2 * Math.PI / 60 * (index + 1 - second))
      this.context.save()
      this.context.restore()
   })
}

标签:控件,rotate,自定义,鸿蒙,index,canvasWidth,context,PI,Math
From: https://blog.csdn.net/Abner_Crazy/article/details/136875511

相关文章

  • 使用Django-Simple-Captcha在Django项目加入验证码模块并自定义样式
    在Django项目中加入验证码功能,通常需要借助第三方库,比如Django-Smple-Captch、Django-reCAPTCHA、DEF-reCAPTCHA、Wagtail-Django-ReCaptcha、Django-Friendly-Captcha等。其中,Django-Smple-Captcha是一个流行的选择,它提供了一个简单而强大的Django应用,无需调用第三方API,......
  • 鸿蒙开发,使用http返回的响应数据无法正常获取 ,利用hilog打印日志一直结果是object或者
    项目场景:这里简述项目相关背景:前后端分离项目,使用鸿蒙做前端,后端SpringBoot写好接口(通过商品分类id查询商品列表),鸿蒙前端页面使用Tabs组件导航,展示商品分类,点击分类标签,查询后端接口,返回对应分类商品列表数据项目场景:鸿蒙开发,使用http返回的响应数据无法正常获取,利用hilo......
  • 深度解析webpack5以及打包实践攻略,看完这篇带你玩转高级自定义打包
    1.webpack5对比webpack4做了哪些优化Webpack5对比Webpack4存在一些重要的优化。Webpack5在性能、构建速度、TreeShaking等方面都有所改进:性能改进:Webpack5在构建速度和性能方面有所提升。这主要是通过改进缓存策略、优化构建算法以及增强的持久化缓存等方式......
  • Vue开发日志:自定义组件:通用开发流程
    自定义组件:通用开发流程通用流程一组概念:key,value,labelProps:required和default同时存在的必要性让我们简单梳理一下通用流程在Vue.js中开发自定义组件的通用流程如下:定义组件模板:创建一个.vue文件,里面包含模板、样式和脚本部分。例如:<!--MyCustomCompone......
  • 鸿蒙HarmonyOS实战-ArkUI组件(Row/Column)
    ......
  • python 教你如何创建一个自定义库 colorlib.py
    目录Colorlib生成代码模块代码导入测试测试一测试二应用测试颜色列表colorList随机颜色元组randcolorTuples随机颜色字串randcolorStringsColor类测试测试一测试二题外话Colorlib有没有碰到过这样的场景:写代码时想要用上丰富的色彩,但苦思冥想搜肠刮肚只记......
  • 自定义类型--结构体、联合体、枚举类型
    **Ladiesandgentlemen**,今天,我们将来进行对自定义类型的学习!目录1.结构的特殊声明2.结构体内存对齐2.1对齐规则2.1.12.1.22.1.32.1.42.2为什么存在内存对齐?1.平台原因(移植原因):2.性能原因:2.3修改默认对齐数3.结构体传参4.结构体实现位段4.1什么......
  • uni-app/小程序自定义导航栏下拉刷新loading图标看不到问题解决
    实际效果图 我们在page.json中开启了自定义导航栏属性和下拉刷新属性后//开启下拉刷新"enablePullDownRefresh":true//自定义导航栏"navigationStyle":"custom"此时,页面中的下拉刷新三个小圆点会被我们的导航栏遮盖住,导致用户下拉刷新看不到loading效果,如下图:......
  • ROS2自定义msg
    在ROS2中,您可以通过编写自己的自定义消息来扩展消息类型。以下是如何创建自定义消息的一般步骤:1.**创建消息文件夹**:在功能包下创建msg的文件夹2.**编写消息文件**:在`msg`文件夹内创建一个`xxx.msg`文件,命名为所需的消息类型,例如`MyCustomMsg.msg`。3.**定义消息结构**:在消......
  • 动态控件之UI和数据加载分离
    一、问题说明比如一个弹框页面中包含listbox控件,弹框页面打开时,先进行listbox初始化,然后再进行数据加载,如果数据加载较慢,这里就会出现,弹框一直无法显示出来,直到数据加载完成,赋值给listbox控件,才会显示。_listbox.ItemsSource=data; 二、解决方式解决方式也简单,就是异步,......