Tips:
-
@Preview装饰器,支持组件可预览;
-
@Component装饰器,自定义组件;
-
Canvas组件的使用;
-
使用RenderingContext在Canvas组件上绘制图形,请参考官方文档:https://developer.huawei.com/consumer/cn/doc/harmonyos-references-V5/ts-canvasrenderingcontext2d-V5
一、自定义闹钟组件
-
新建component目录用来存放自定义组件,在其下再新建ArkTS文件,命名为ClockArea.ets。
2.页面主要包括画布组件Canvas,点击画布切换显示时钟表盘或数字时间样式,对其属性设置及布局如下。
@State showClock: boolean = true; // 是否显示时钟
private renderContextSettings: RenderingContextSettings = new RenderingContextSettings(true);
private renderContext: CanvasRenderingContext2D = new CanvasRenderingContext2D(this.renderContextSettings);
// 画布尺寸
private canvasSize: number = 252;
// 绘制间隔时间
private drawInterval: number = -1;
private clockRadius: number = this.canvasSize / 2 - 2;
build() {
Column() {
Canvas(this.renderContext)
.width(this.canvasSize)
.aspectRatio(1)
.onClick(() => {
this.showClock = !this.showClock;
})
.onReady(() => {
if (this.drawInterval === -1) {
// 开始绘制
this.startDrawTask();
}
})
}
}
3.启动定时任务,每秒绘制一次闹钟,与时间同步秒针动态在走的效果。
/**
* 启动定时绘制任务
*/
private startDrawTask(): void {
// console.log("开始绘制");
this.renderContext.translate(this.canvasSize / 2, this.canvasSize / 2);
this.drawClockArea()
this.drawInterval = setInterval(() => {
this.drawClockArea()
}, 1000);
}
4.定义绘制闹钟区域的方法drawClockArea
/**
* 绘制闹钟区域
*/
private drawClockArea(): void {
// console.log("绘制时区");
let date = new Date();
let hour = date.getHours();
let minute = date.getMinutes();
let second = date.getSeconds();
this.renderContext.clearRect(
-this.canvasSize,
-this.canvasSize / 2,
this.canvasSize * 2,
this.canvasSize
);
if (this.showClock) {
this.drawClockPan();
this.drawClockHands(
30 * (hour > 12 ? hour - 12 : hour)
+ minute / 12 * 6,
BaseConstant.HOUR_HAND_IMAGE_URL);
this.drawClockHands(minute * 6, BaseConstant.MINUTE_HAND_IMAGE_URL);
this.drawClockHands(second * 6, BaseConstant.SECOND_HAND_IMAGE_URL);
} else {
// 回显数字时间
this.drawTimeHHMMSS(hour, minute, second);
}
}
5.定义绘制闹钟的表盘的方法drawClockPan,其实就是一张表盘时刻的背景图。
/**
* 绘制表盘
*/
private drawClockPan(): void {
let imgWidth = this.clockRadius * 2;
let secondImg = new ImageBitmap(BaseConstant.CLOCK_PAN_IMAGE_URL);
this.renderContext.beginPath();
this.renderContext.drawImage(
secondImg,
-this.clockRadius,
-this.clockRadius,
imgWidth, imgWidth);
this.renderContext.restore();
}
6.定义绘制表针的方法drawClockHands,包括时针、分针和秒针,每个针对应一个图片。
/**
* 绘制表针:时针、分针、秒针
*/
private drawClockHands(degree: number, handImgRes: string): void {
let imgWidth = 10;
let handImg = new ImageBitmap(handImgRes);
let theta = (degree + 180) * Math.PI / 180;
this.renderContext.save();
this.renderContext.rotate(theta);
this.renderContext.beginPath();
this.renderContext.drawImage(
handImg,
-imgWidth / 2,
-this.clockRadius,
imgWidth,
this.clockRadius * 2);
this.renderContext.restore();
}
7.定义绘制数字时间的方法,时间的显示格式为24小时制HH:MM:SS。
/**
* 绘制数字时间HH:MM:SS
*/
private drawTimeHHMMSS(hour: number, minute: number, second: number): void {
let hh = hour > 9 ? hour.toString() : "0" + hour;
let mm = minute > 9 ? minute.toString() : "0" + minute;
let ss = second > 9 ? second.toString() : "0" + second;
let time = `${hh}:${mm}:${ss}`;
this.renderContext.save();
this.renderContext.font = SizeUtil.getPx($r("app.float.clock_time_font_size")) + "px";
this.renderContext.beginPath();
this.renderContext.textAlign = "center";
this.renderContext.fillText(time, 0, 0);
this.renderContext.restore();
}
到此,会发现以上代码缺少2个重要的类文件,分别是BaseConstant.ets和SizeUtil.ets,以及资源文件float.json的参数定义。
二、定义常量类文件BaseConstant.ets
新建目录constants,在其下新建ArkTS文件BaseConstant.ets。程序中的常量可以定义在这个类文件中,比如:图片路径等。
export class BaseConstant {
static readonly CLOCK_PAN_IMAGE_URL: string = "images/icon_clock_pan.png";
static readonly HOUR_HAND_IMAGE_URL: string = "images/icon_hour_hand.png";
static readonly MINUTE_HAND_IMAGE_URL: string = "images/icon_minute_hand.png";
static readonly SECOND_HAND_IMAGE_URL: string = "images/icon_second_hand.png";
}
三、定义单位转换类文件SizeUtil.ets
新建目录utils,在其下新建ArkTS文件SizeUtil.ets。为什么要封装这个单位转换公共类,可参考我的帖子:https://developer.huawei.com/consumer/cn/forum/topic/0208151714177357329?fid=0101587866109860105
import display from '@ohos.display';
import { GlobalContext } from './GlobalContext';
let context = getContext(this);
const DESIGN_WIDTH = 360; // 设计稿宽度
const DESIGN_HEIGHT = 780; // 设计稿高度
/**
* 尺寸适配工具类
*/
export default class SizeUtil {
/**
* 尺寸适配
* @param value 设计稿尺寸
*/
static adaptSize(value: number): number {
let deviceDisplay = GlobalContext.getContext().getObject("globalDisplay") as display.Display;
let widthScale = deviceDisplay.width / DESIGN_WIDTH;
let virtualHeight = DESIGN_HEIGHT * widthScale;
let designDim = Math.sqrt(DESIGN_WIDTH * DESIGN_WIDTH + DESIGN_HEIGHT * DESIGN_HEIGHT);
let virtualDim = Math.sqrt(deviceDisplay.width * deviceDisplay.width + virtualHeight * virtualHeight);
return virtualDim * value / designDim; // 放缩后长度
}
/**
* 获取px
* @param value 设计稿尺寸
*/
static getPx(value: Resource): number {
console.log("context:", context);
let beforeVp = context.resourceManager.getNumber(value.id);
return SizeUtil.adaptSize(beforeVp);
}
/**
* 获取vp
* @param value 设计稿尺寸
*/
static getVp(value: Resource): number {
let beforeVp = context.resourceManager.getNumber(value.id);
return px2vp(SizeUtil.adaptSize(beforeVp));
}
/**
* 获取fp
* @param value 设计稿尺寸
*/
static getFp(value: Resource): number {
let beforeFp = context.resourceManager.getNumber(value.id);
return px2fp(SizeUtil.adaptSize(beforeFp));
}
}
四、定义全局上下文类文件GlobalContext.ets
在utils目录下新建ArkTS文件GlobalContext.ets。
/**
* 全局上下文
*/
export class GlobalContext {
private constructor() {
}
private static instance: GlobalContext;
private objects = new Map<string, Object>();
/**
* 获取全局上下文
*/
public static getContext(): GlobalContext {
if (!GlobalContext.instance) {
GlobalContext.instance = new GlobalContext();
}
return GlobalContext.instance;
}
/**
* 获取对象
*/
getObject(name: string): Object | undefined {
return this.objects.get(name);
}
/**
* 设置对象
*/
setObject(key: string, objectClass: Object): void {
this.objects.set(key, objectClass);
}
}
五、资源文件float.json
在文件中定义常用的数值变量,比如:显示数字时间的字体大小。
{
"name": "clock_time_font_size",
"value": "50"
},
请查阅官方文档:https://developer.huawei.com/consumer/cn/doc/harmonyos-guides/resource-usage-0000001820880417
六、图片文件
新建images目录,在其下添加已设计好的表盘、时针、分针和秒针的图片。
(图片素材见文章顶部的附件)
七、运行效果
注意:需要在真机上才可见秒针走动的效果,及点击切换显示数字时钟。