鸿蒙-canvas-画时钟

打印 上一主题 下一主题

主题 941|帖子 941|积分 2833

前言

你在 Android 上能画出来的东西,在鸿蒙上画不出来?
画个时钟嘛,有啥难的?
你行你上!
给钱就上!
给钱?早说嘛,来来来,现在就画
预备

画时钟需要画哪些元素?
圆圈、直线,没了,就这些,暂时看一下canvas 相关的 api,这不都有么?直接画。
看看需要用的方法
  1. arc(x: number, y: number, radius: number, startAngle: number, endAngle: number, counterclockwise?: boolean): void
复制代码
看下参数含义
参数类型必填默认值描述xnumber是0弧线圆心的x坐标值。ynumber是0弧线圆心的y坐标值。radiusnumber是0弧线的圆半径。startAnglenumber是0弧线的起始弧度。endAnglenumber是0弧线的终止弧度。counterclockwiseboolean否false是否逆时针绘制圆弧。 弧度制,一圈是 2π,这个需要留意一下,另有endAngle,是终止弧度,而不是需要画多少弧度,浅浅的尝试。
  1. struct ClockViewTest {
  2.   private settings: RenderingContextSettings = new RenderingContextSettings(true)
  3.   private canvasRendering: CanvasRenderingContext2D = new CanvasRenderingContext2D(this.settings)
  4.   build() {
  5.     Canvas(this.canvasRendering).width("100%").height("100%")
  6.       .onReady(() => {
  7.         let width = this.canvasRendering.width
  8.         let height = this.canvasRendering.height
  9.         let centerX = width / 2
  10.         let centerY = height / 2
  11.         //取长宽中小的一个做直径
  12.         let maxRadius = Math.min(width, height) / 2
  13.         //设置线的粗细
  14.         this.canvasRendering.lineWidth = 4
  15.         this.canvasRendering.arc(centerX, centerY, maxRadius-50, 0, 1, false)
  16.         //设置线的颜色
  17.         this.canvasRendering.strokeStyle = "#ff0000"
  18.         this.canvasRendering.stroke()
  19.         this.canvasRendering.beginPath()
  20.         this.canvasRendering.arc(centerX, centerY, maxRadius - 30, 0, 1, true)
  21.         //设置线的颜色
  22.         this.canvasRendering.strokeStyle = "#00ff00"
  23.         this.canvasRendering.stroke()
  24.       })
  25.   }
  26. }
复制代码
效果是如许的:

画直线就不用多说了,开干~~
分析

构成部分

指针是需要根据时间变化来转动的,表盘画好一次就不需要重绘了,偷个懒,搞两个 canvas 摞起来,底层画表盘,上层画指针,时间变了只重画上层指针就行了。
数值计算

简单的三角函数,但要留意是弧度制,数值别搞错了。
别的需要留意的是画布左上角坐标是(0,0),右下角坐标为(width,height)。
过程


  • 先画一大一小两个圆圈构成一个圆环。
  • 再划线把圆环均分 60 份,每 5 条线加粗一下。
  • 再把圆周分成 12 份,对应位置画上1~12 数字。
  • 获取当前时间,计算出指针位置,划线。
  • 定时更新指针位置。
  • 竣事。
开始

第一步 画圆环

  1. Canvas(this.canvasRendering).width("100%").height("100%")
  2.   .onReady(() => {
  3.     let width = this.canvasRendering.width
  4.     let height = this.canvasRendering.height
  5.     let centerX = width / 2
  6.     let centerY = height / 2
  7.     //取长宽中小的一个做直径
  8.     let maxRadius = Math.min(centerX, centerY)
  9.     //留一些外边距
  10.     let outerCircleRadius = maxRadius - 20
  11.     this.canvasRendering.strokeStyle = "#1b91e0"
  12.     this.canvasRendering.lineWidth = 2
  13.     //最中间的小圈圈
  14.     this.canvasRendering.arc(centerX, centerY, 10, 0, Math.PI * 2, false)
  15.     this.canvasRendering.stroke()
  16.     //画内圈
  17.     this.canvasRendering.beginPath()
  18.     let innerCircleRadius = outerCircleRadius -20
  19.     this.canvasRendering.arc(centerX, centerY, innerCircleRadius, 0, Math.PI * 2, false)
  20.     this.canvasRendering.stroke()
  21.     //画外圈
  22.     this.canvasRendering.beginPath()
  23.     this.canvasRendering.arc(centerX, centerY, outerCircleRadius, 0, Math.PI * 2, false);
  24.     this.canvasRendering.stroke()
  25.    
  26.   })
复制代码
效果图

看着还行,颜色和粗细大家自己调。
第二步 画格子

  1. //画 60 个格子,5 的倍数则线条粗一些
  2. let perMinuteDegree = Math.PI * 2 / 60
  3. for (let i = 1;i <= 60; i++) {
  4.   //结束坐标,也就是在外圆上的点
  5.   let endX = centerX + Math.sin(i * perMinuteDegree) * outerCircleRadius
  6.   let endY = centerY + Math.cos(i * perMinuteDegree + Math.PI) * outerCircleRadius
  7.   //起始坐标,也就是在内圆上的点
  8.   let startX = centerX + Math.sin(i * perMinuteDegree) * innerCircleRadius
  9.   let startY = centerY + Math.cos(i * perMinuteDegree + Math.PI) * innerCircleRadius
  10.   this.canvasRendering.strokeStyle = "#000000"
  11.   let path2D = new Path2D()
  12.   path2D.moveTo(startX, startY)
  13.   path2D.lineTo(endX, endY)
  14.   if (i % 5 == 0) {
  15.     this.canvasRendering.lineWidth = 6
  16.   } else {
  17.     this.canvasRendering.lineWidth = 2
  18.   }
  19.   this.canvasRendering.stroke(path2D)
  20. }
复制代码
效果图

敷衍了事,不太好看。
这里需要留意一下,画布是以垂直向下为 Y 轴的正方向,计算时加了 Math.PI 弧度改正一下
第三步 画数字

  1. //画 1~12 数字圆形分布
  2. this.canvasRendering.font = "40px"
  3. let perNumberDegree = Math.PI * 2 / 12
  4. let numberRadius = outerCircleRadius - 40
  5. for (let i = 1;i <= 12; i++) {
  6.   let x = centerX + Math.sin(i * perNumberDegree) * numberRadius
  7.   let y = centerY + Math.cos(i * perNumberDegree + Math.PI) * numberRadius
  8.   let text: string = i + ""
  9.   this.canvasRendering.fillStyle = "#000000"
  10.   let textMetrics: TextMetrics = this.canvasRendering.measureText(text)
  11.   //填充文字时,传入的坐标是文字的左下角坐标
  12.   this.canvasRendering.fillText(text, x-textMetrics.width/2 , y+textMetrics.height/2)
  13.   //把下面这两行注释掉就没有小方块了
  14.   this.canvasRendering.fillStyle = "#aaff6134"
  15.   this.canvasRendering.fillRect(x,y,textMetrics.width,textMetrics.height)
  16. }
复制代码
效果图

图上的方块是为了对比画笔墨和画方块的坐标区别展示出来的:添补笔墨时传入的坐标是笔墨左下角的坐标,而画方块时是传入的方块左上角坐标,这里留意一下就好了,代码中测量了一下笔墨宽高,粗暴的做了一下纠偏。
第四、五步 画指针&定时更新

上面也说要把指针画在别的一个 canvas 上,减少一下绘制时的内容,没做对比,也不知道有没有作用。
预备别的个画布,把两个画布用 Stack 包一下。
  1. Canvas(this.canvasRenderingClock).width("100%").height("100%").onReady(() => {
  2.   this.timer = setInterval(function(){
  3.     let date: Date = new Date()
  4.     this.minute = date.getMinutes()
  5.     this.hour = date.getHours()
  6.     this.second = date.getSeconds()
  7.     this.draw()
  8.   }.bind(this), 500)
  9. })
复制代码
这里需要把第一块代码中的 innerCircleRadius 变量提到外部,作为类成员两个画布共用一下,主要是计算指针尽头坐标用的。centerX 和 centerY 无所谓,只要两个画布对齐了,用哪个都行,这里照旧提到了外部,用的第一块画布的。
  1. private draw() {
  2.   清空一下画布
  3.   this.canvasRenderingClock.clearRect(0, 0, this.centerX * 2, this.centerY * 2)
  4.   //画秒针
  5.   //计算秒针的角度
  6.   let secondDegree = Math.PI * 2 / 60 * this.second
  7.   let secondStartX = this.centerX
  8.   let secondStartY = this.centerY
  9.   let secondEndX = this.centerX + Math.sin(secondDegree) * this.innerCircleRadius
  10.   let secondEndY = this.centerY + Math.cos(secondDegree + Math.PI) * this.innerCircleRadius
  11.   let secondPath = new Path2D()
  12.   secondPath.moveTo(secondStartX, secondStartY)
  13.   secondPath.lineTo(secondEndX, secondEndY)
  14.   this.canvasRenderingClock.lineWidth = 2
  15.   this.canvasRenderingClock.stroke(secondPath)
  16.   //画分针 颜色弄点透明度,要不然重合的时候看不清楚
  17.   //秒针走一圈,分针走一格,其实可以忽略不计
  18.   let minuteDegree = Math.PI * 2 / 60 * this.minute
  19.   let minuteStartX = this.centerX
  20.   let minuteStartY = this.centerY
  21.   let minuteEndX = this.centerX + Math.sin(minuteDegree) * (this.innerCircleRadius / 5 * 4)
  22.   let minuteEndY = this.centerY + Math.cos(minuteDegree + Math.PI) * (this.innerCircleRadius / 5 * 4)
  23.   let minutePath = new Path2D()
  24.   minutePath.moveTo(minuteStartX, minuteStartY)
  25.   minutePath.lineTo(minuteEndX, minuteEndY)
  26.   this.canvasRenderingClock.strokeStyle = "#aa1b91e0"
  27.   this.canvasRenderingClock.lineWidth = 4
  28.   this.canvasRenderingClock.stroke(minutePath)
  29.   //画时针
  30.   //分针走一圈,时针走 5 小格
  31.   let hourDegree = Math.PI * 2 / 12 * this.hour + this.minute / 60 * Math.PI * 2 / 12
  32.   let hourStartX = this.centerX
  33.   let hourStartY = this.centerY
  34.   let hourEndX = this.centerX + Math.sin(hourDegree) * (this.innerCircleRadius / 4 * 3)
  35.   let hourEndY = this.centerY + Math.cos(hourDegree + Math.PI) * (this.innerCircleRadius / 4 * 3)
  36.   let hourPath = new Path2D()
  37.   hourPath.moveTo(hourStartX, hourStartY)
  38.   hourPath.lineTo(hourEndX, hourEndY)
  39.   this.canvasRenderingClock.lineWidth = 6
  40.   this.canvasRenderingClock.strokeStyle = "#aa39d167"
  41.   this.canvasRenderingClock.stroke(hourPath)
  42. }
复制代码
计算指针角度的时候也偷懒了,时针只思量了当前分钟数,没有思量秒数,现实差不多,先如许吧。
最后一步

效果图

就先如许吧,勉勉强强,可以自己调调颜色,调调样式,大概搞一些图片来代替这些元素也行。
源码在这里 https://github.com/huangyuanlove/HelloArkUI/blob/main/entry/src/main/ets/pages/playground/AlarmClockPage.ets ,
https://gitee.com/huangyuan/HelloArkUI/blob/main/entry/src/main/ets/pages/playground/AlarmClockPage.ets
仓库所在:https://github.com/huangyuanlove/HelloArkUI
https://gitee.com/huangyuan/HelloArkUI

以上

免责声明:如果侵犯了您的权益,请联系站长,我们会及时删除侵权内容,谢谢合作!更多信息从访问主页:qidao123.com:ToB企服之家,中国第一个企服评测及商务社交产业平台。

本帖子中包含更多资源

您需要 登录 才可以下载或查看,没有账号?立即注册

x
回复

使用道具 举报

0 个回复

倒序浏览

快速回复

您需要登录后才可以回帖 登录 or 立即注册

本版积分规则

宁睿

金牌会员
这个人很懒什么都没写!

标签云

快速回复 返回顶部 返回列表