场景介绍
常见的时钟呈现方式有两种,一种是表盘方式,一种是数字方式。用户可根据个人喜好在两种形式间进行切换。本例即为大家讲解如何开发上述两种钟表样式,以供参考。
效果呈现
本例最终效果如下:
运行环境
本例基于以下环境开发,开发者也可以基于其他适配的版本进行开发:
- IDE: DevEco Studio 3.1 Beta2
- SDK: Ohos_sdk_public 3.2.11.9 (APIVersion 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')
}
}
参考
Canvas
CanvasRenderingContext2D对象
TextClock
我这边特意整理了《鸿蒙语法ArkTS、TypeScript、ArkUI、实战开发视频教程》以及《鸿蒙生态应用开发白皮书V2.0PDF》《鸿蒙开发学习手册》(共计890页)鸿蒙开发资料等…希望对大家有所帮助:https://docs.qq.com/doc/DZVVBYlhuRkZQZlB3
鸿蒙语法ArkTS、TypeScript、ArkUI等…视频教程:https://docs.qq.com/doc/DZVVBYlhuRkZQZlB3
OpenHarmony APP开发教程步骤:https://docs.qq.com/doc/DZVVBYlhuRkZQZlB3
鸿蒙生态应用开发白皮书V2.0PDF:https://docs.qq.com/doc/DZVVkRGRUd3pHSnFG
应用开发中级就业技术:https://docs.qq.com/doc/DZVVBYlhuRkZQZlB3
应用开发中高级就业技术:https://docs.qq.com/doc/DZVVBYlhuRkZQZlB3
南北双向高工技能基础:https://docs.qq.com/doc/DZVVBYlhuRkZQZlB3
全网首发-工业级 南向设备开发就业技术:https://docs.qq.com/doc/DZVVBYlhuRkZQZlB3
《鸿蒙开发学习手册》:
如何快速入门:https://docs.qq.com/doc/DZVVBYlhuRkZQZlB3
1.基本概念
2.构建第一个ArkTS应用
3.……
开发基础知识:https://docs.qq.com/doc/DZVVBYlhuRkZQZlB3
1.应用基础知识
2.配置文件
3.应用数据管理
4.应用安全管理
5.应用隐私保护
6.三方应用调用管控机制
7.资源分类与访问
8.学习ArkTS语言
9.……
基于ArkTS 开发:https://docs.qq.com/doc/DZVVBYlhuRkZQZlB3
1.Ability开发
2.UI开发
3.公共事件与通知
4.窗口管理
5.媒体
6.安全
7.网络与链接
8.电话服务
9.数据管理
10.后台任务(Background Task)管理
11.设备管理
12.设备使用信息统计
13.DFX
14.国际化开发
15.折叠屏系列
16.……