14. 利用Canvas自制时钟组件
1. 说明
在自定义时钟组件时,使用到的基本控件主要是Canvas,在绘制相关元素时有两种方式:一种时在同一个canvas中绘制所有的部件元素,这样需要不断的对画笔和画布的属性进行保存和恢复,容易混乱;另一种就是创建多个canvas组件,每一部分的元素绘制都在各自的画布进行绘制,逻辑比较清晰,但是canvas组件会相对较多,本文使用的是第二种方式。
效果展示:

2. 整体代码
import QtQuick 2.15
import QtQuick.Controls 2.15Item{id:rootimplicitWidth: 400implicitHeight: implicitWidth// 尺寸属性property real outerCircleRadius:root.width / 2.05property real innerCircleRadius:root.width / 2.05// 颜色属性property color bgColor:"white"property color outerColor:"black"property color innerColor:"black"property color innerRootColor:"lightSlateGray"property color innerLineColorL:"#484D58"property color innerLineColorS:"#63677A"property color textColor:"black"property color hourLineColor:"#484D58"property color minuteLineColor:"#484D58"property color secondLineColor:"red"property color timeTxtColor:"black"// 时间属性property var hoursproperty var minutesproperty var secondsproperty var currentTimeproperty alias hoursAngle:hourLine.angleproperty alias minutesAngle:minuteLine.angleproperty alias secondsAngle:secondLine.angle// 组件加载完成后先初始化当前时间Component.onCompleted: {calculateAngle()}// 时间计算function calculateAngle(){var date = new Date()hours = date.getHours()// 模除得到12小时制的小时数hours = hours % 12minutes = date.getMinutes()seconds = date.getUTCSeconds()currentTime = hours + ":" + minutes + ":" + secondshoursAngle = Math.PI*2/12*hours+Math.PI*2*minutes/12/60-Math.PIminutesAngle = Math.PI*2*minutes/60 + Math.PI*2*seconds/60/60 -Math.PIsecondsAngle = Math.PI*2*seconds/60-Math.PI}// 绘制背景Canvas{id:bgCirclewidth: root.widthheight: root.heightanchors.centerIn: parentonPaint: {// 绘制背景var ctx = getContext("2d") ctx.save()ctx.lineWidth = root.width/50 ctx.fillStyle = bgColorctx.beginPath()ctx.arc(root.width/2,root.height/2,outerCircleRadius,0,Math.PI * (12/6))ctx.fill()ctx.restore()}}// 绘制圆环轮廓Canvas{id:outerCirclewidth: root.widthheight: root.heightanchors.centerIn: parentonPaint: {var ctx = getContext("2d") //创建画师//为画师创建画笔并设置画笔属性ctx.lineWidth = root.width/50 //设置画笔粗细ctx.strokeStyle = outerColor //设置画笔颜色ctx.beginPath() //每次绘制调用此函数,重新设置一个路径// 按照钟表刻度进行划分,一圈是Math.PI * 2,分成12个刻度,每个刻度占用 1/6// canvas绘制圆弧,是按照顺时针绘制,起点默认在三点钟方向ctx.arc(root.width/2,root.height/2,outerCircleRadius,0,Math.PI * (12/6))ctx.stroke() //根据strokeStyle对边框进行描绘}}// 绘制圆环内衬Canvas{id:innerCirclewidth: root.widthheight: root.heightanchors.centerIn: parentproperty real endAngle:Math.PI * (12/6)onPaint: {var ctx = getContext("2d") ctx.save()ctx.lineWidth = root.width/50 ctx.strokeStyle = innerColor ctx.beginPath() ctx.arc(root.width/2,root.height/2,innerCircleRadius,0,endAngle)ctx.stroke() ctx.restore()// 绘制指针根部圆圈ctx.save()ctx.lineWidth = root.width/50 ctx.fillStyle = innerRootColorctx.beginPath()ctx.arc(root.width/2,root.height/2,innerCircleRadius/16,0,endAngle)ctx.fill()ctx.restore()}}// 绘制刻度线Canvas{id:innerLinewidth: root.widthheight: root.heightanchors.centerIn: parentproperty real lineNums:60onPaint: {var ctx = getContext("2d") for (var i = 0; i <= lineNums; ++i){ctx.beginPath();var angle = 2 * Math.PI / 60 * i;var dx = Math.cos(angle)*(outerCircleRadius-15);var dy = Math.sin(angle)*(outerCircleRadius-15);var dx2 = Math.cos(angle)*(outerCircleRadius-7);var dy2 = Math.sin(angle)*(outerCircleRadius-7);if (i % 5 === 0){ctx.lineWidth = root.width/100ctx.strokeStyle = innerLineColorL}else{ctx.lineWidth = root.width/200ctx.strokeStyle = innerLineColorS}ctx.moveTo(root.width/2+dx,root.height/2+dy);ctx.lineTo(root.width/2+dx2,root.height/2+dy2);ctx.stroke();}}}// 绘制数字Canvas{id:drawTextwidth: root.widthheight: root.heightanchors.centerIn: parentproperty var numbers : [1,2,3,4,5,6,7,8,9,10,11,12]onPaint: {var ctx = getContext("2d") ctx.font = "18px Arial";ctx.textAlign = "center";ctx.textBaseline = "middle";for(var i = 0; i < 12; ++i){ctx.fillStyle = textColorvar angle = 2 * Math.PI / 12 * numbers[i] - 3.14 / 2;var dx = Math.cos(angle)*(outerCircleRadius-30);var dy = Math.sin(angle)*(outerCircleRadius-30);ctx.fillText(numbers[i],root.width/2 + dx,root.height / 2 + dy);ctx.fill()}}}// 绘制时针线Canvas{id:hourLinewidth: root.widthheight: root.heightanchors.centerIn: parentproperty real angleonPaint: {var ctx = getContext("2d") // 先清空画布上之前的内容ctx.clearRect(0,0,width,height)ctx.save()ctx.beginPath()ctx.lineWidth = root.width/100ctx.strokeStyle=hourLineColor// 平移坐标点(注意:坐标系原点先平移,再旋转)ctx.translate(root.width/2,root.height/2)// 旋转坐标系ctx.rotate(angle)// 坐标原点变化之后再进行实际的绘图ctx.moveTo(0,-20);ctx.lineTo(0,outerCircleRadius / 2 - 15);ctx.stroke()ctx.restore()}}// 绘制分针线Canvas{id:minuteLinewidth: root.widthheight: root.heightanchors.centerIn: parentproperty real angleonPaint: {var ctx = getContext("2d") ctx.clearRect(0,0,width,height)ctx.save()ctx.beginPath()ctx.lineWidth = root.width/100ctx.strokeStyle=minuteLineColor// 平移坐标点(注意:坐标系原点先平移,再旋转)ctx.translate(root.width/2,root.height/2)// 旋转坐标系ctx.rotate(angle)// 坐标原点变化之后再进行实际的绘图ctx.moveTo(0,-25);ctx.lineTo(0,outerCircleRadius / 2 - 5);ctx.stroke()ctx.restore()}}// 绘制秒针线Canvas{id:secondLinewidth: root.widthheight: root.heightanchors.centerIn: parentproperty real angleonPaint: {var ctx = getContext("2d") ctx.clearRect(0,0,width,height)ctx.save()ctx.beginPath()ctx.lineWidth = root.width/100ctx.strokeStyle=secondLineColor// 平移坐标点(注意:坐标系原点先平移,再旋转)ctx.translate(root.width/2,root.height/2)// 旋转坐标系ctx.rotate(angle)// 坐标原点变化之后再进行实际的绘图ctx.moveTo(0,-30);ctx.lineTo(0,outerCircleRadius / 1.5);ctx.stroke()ctx.restore()}}// 当前时间显示Text{id:timeTxty:root.height * 0.75anchors.horizontalCenter: root.horizontalCentertext: currentTimefont.pixelSize: root.width/12color: timeTxtColor}// 使用定时器(每秒钟进行计算)Timer{id:angleCalinterval: 1000repeat: truerunning: trueonTriggered: {calculateAngle()hourLine.requestPaint()minuteLine.requestPaint()secondLine.requestPaint()}}
}相关文章:
14. 利用Canvas自制时钟组件
1. 说明 在自定义时钟组件时,使用到的基本控件主要是Canvas,在绘制相关元素时有两种方式:一种时在同一个canvas中绘制所有的部件元素,这样需要不断的对画笔和画布的属性进行保存和恢复,容易混乱;另一种就是…...
微信小程序使用云存储和Markdown开发页面
最近想在一个小程序里加入一个使用指南的页面,考虑到数据存储和减少页面的开发工作量,决定尝试在云存储里上传Markdown文件,微信小程序端负责解析和渲染。小程序端使用到一个库Towxml。 Towxml Towxml是一个可将HTML、Markdown转为微信小程…...
【C++】运算符重载 | 赋值运算符重载
Ⅰ. 运算符重载 引入 ❓什么叫运算符重载? 就是:运用函数,将现有的运算符重新定义,使其能满足各种自定义类型的运算。 回想一下,我们以前运算的对象是不是都是int、char这种内置类型? 那我们自定义的“…...
Python学习 -- 类对象从创建到常用函数
在Python编程中,类是一种强大的工具,用于创建具有共同属性和行为的对象。本篇博客将详细介绍Python中类和对象的创建,类的属性和方法,以及一些常用的类函数,通过丰富的代码例子来帮助读者深入理解。 一、类和对象的创…...
数组分割(2023省蓝桥杯)n种讨论 JAVA
目录 1、题目描述:2、前言:3、动态规划(bug):3、递归 剪枝(超时):4、数学(正解): 1、题目描述: 小蓝有一个长度为 N 的数组 A [A0, A1,…, AN−…...
很好的启用window10专业版系统自带的远程桌面
启用window10专业版系统自带的远程桌面 文章目录 启用window10专业版系统自带的远程桌面前言1.找到远程桌面的开关2. 找到“应用”项目3. 打开需要远程操作的电脑远程桌面功能 总结 前言 Windows操作系统作为应用最广泛的个人电脑操作系统,在我们身边几乎随处可见。…...
TCP定制协议,序列化和反序列化
目录 前言 1.理解协议 2.网络版本计算器 2.1设计思路 2.2接口设计 2.3代码实现: 2.4编译测试 总结 前言 在之前的文章中,我们说TCP是面向字节流的,但是可能对于面向字节流这个概念,其实并不理解的,今天我们要介…...
YOLOX在启智AI GPU/CPU平台部署笔记
文章目录 1. 概述2. 部署2.1 拉取YOLOX源码2.2 拉取模型文件yolox_s.pth2.3 安装依赖包2.4 安装yolox2.5 测试运行2.6 运行报错处理2.6.1 ImportError: libGL.so.1: cannot open shared object file: No such file or directory2.6.2 ImportError: libgthread-2.0.so.0: cannot…...
23种设计模式攻关
👍一、创建者模式 🔖1.1、单例模式 单例模式(Singleton Pattern),用于确保一个类只有一个实例,并提供全局访问点。 在某些情况下,我们需要确保一个类只能有一个实例,比如数据库连接…...
【jsthreeJS】入门three,并实现3D汽车展示厅,附带全码
首先放个最终效果图: 三维(3D)概念: 三维(3D)是一个描述物体在三个空间坐标轴上的位置和形态的概念。相比于二维(2D)只有长度和宽度的平面,三维增加了高度或深度这一维度…...
unity将结构体/列表与json字符串相互转化
编写Unity程序时,面对大量需要传输或者保存的数据时,为了避免编写重复的代码,故采用NewtonJson插件来将定义好的结构体以及列表等转为json字符串来进行保存和传输。 具体代码如下: using System; using System.IO; using Newtons…...
【Vue】vue2项目使用swiper轮播图2023年8月21日实战保姆级教程
提示:文章写完后,目录可以自动生成,如何生成可参考右边的帮助文档 文章目录 前言一、npm 下载swiper二、使用步骤1.引入库声明变量2.编写页面3.执行js 总结 前言 swiper轮播图官网 参考文章,最好先看完他的介绍,再看…...
【算法日志】贪心算法刷题:单调递增数列,贪心算法总结(day32)
代码随想录刷题60Day 目录 前言 单调递增数列 贪心算法总结 前言 今天是贪心算法刷题的最后一天,今天本来是打算刷两道题,其中的一道hard题做了好久都没有做出来(主要思路错了)。然后再总结一下。 单调递增数列 int monotoneIncreasingDigits(int n…...
MATLAB算法实战应用案例精讲-【深度学习】模型压缩
目录 模型压缩概述 1. 为什么需要模型压缩 2. 模型压缩的基本方法 Patient-KD 1. Patient-KD 简介...
Matlab使用
Matlab使用 界面介绍 新建脚本:实际上就是新建一个新建后缀为.m的文件 新建编辑器:ctrlN 打开:打开最近文件,以找到最近写过的文件 点击路径,切换当前文件夹 预设:定制习惯用的界面 常见简单指令 ;…...
BladeX多数据源配置
启用多租户数据库隔离,会默认关闭mybatis-plus多数据源插件的启动,从而使用自定义的数据源识别 若不需要租户数据库隔离只需要字段隔离,而又需要用到多数据源的情况,需要前往LauncherService单独配置 数据源切换失败 详情请看说明…...
go里面关于超时的设计
设想一下你在接收源源不断的数据,如果有700ms没有收到,则认为是一个超时,需要做出处理。 逻辑上可以设计一个grouting,里面放一个通道,每收到一条数据进行相应处理。通道中夹杂一个timer定时器的处理,若通道在700ms内…...
Qt下使用ModbusTcp通信协议进行PLC线圈/保持寄存器的读写(32位有符号数)
文章目录 前言一、引入Modbus模块二、Modbus设备的连接三、各寄存器数据的读取四、各寄存器数据的写入五、示例完整代码总结 前言 本文主要讲述了使用Qt的Modbus模块来进行ModbusTcp的通信,实现对PLC的线圈寄存器和保持寄存器的读写,基于TCP/IP的Modbus…...
ElasticSearch学习2
1、索引的操作 1、创建索引 对ES的操作其实就是发送一个restful请求,kibana中在DevTools中进行ES操作 创建索引时需要注意ES的版本,不同版本的ES创建索引的语句略有差别,会导致失败 如下创建一个名为people的索引,settings&…...
3D角色展示
先看效果: 再看代码: <!DOCTYPE html> <html lang"en"> <head><meta charset"UTF-8"><title>3D卡片悬停</title><style>font-face {font-family: "Exoct";src: url("htt…...
基于电容触摸与接近传感的无接触MIDI控制器设计与实现
1. 项目概述与核心价值如果你玩过电子乐器,或者对音乐制作、交互装置感兴趣,那你一定对MIDI控制器不陌生。传统的MIDI控制器,无论是键盘、打击垫还是旋钮,大多依赖于物理接触——你得实实在在地按下去、扭动它。但有没有想过&…...
Go语言树结构:二叉树与AVL树
Go语言树结构:二叉树与AVL树 1. 二叉树实现 type TreeNode struct {Val intLeft *TreeNodeRight *TreeNode }type BinaryTree struct {Root *TreeNode }func (t *BinaryTree) Insert(val int) {t.Root insert(t.Root, val) }func insert(node *TreeNode, val in…...
用Python手把手模拟一个混淆电路(Garbled Circuit):从Alice和Bob的故事理解安全多方计算
用Python手把手模拟一个混淆电路:从Alice和Bob的故事理解安全多方计算 在数字时代,数据隐私的重要性日益凸显。想象这样一个场景:两位商业伙伴Alice和Bob希望共同计算一个商业决策,但都不愿意透露自己的核心数据。这种需求催生了安…...
GUID partition table, GPT 磁盘分区表
GPT分割表 LBA0 (MBR 兼容区块) 与 MBR 模式相似的,这个兼容区块也分为两个部份,一个就是跟之前 446 bytes 相似的区块,存储了第一阶段的开机管理程式! 而在原本的分割表的纪录区内,这个兼容模式仅放入一个特殊标志的分割,用来表示此磁盘为 GPT 格式之意。而不懂 GPT 分割…...
从质谱数据到生物学洞察:MZmine3如何重新定义代谢组学分析效率
从质谱数据到生物学洞察:MZmine3如何重新定义代谢组学分析效率 【免费下载链接】mzmine3 mzmine source code repository 项目地址: https://gitcode.com/gh_mirrors/mz/mzmine3 在当今代谢组学研究中,科研人员面临着从海量质谱数据中提取生物学意…...
轻松管理Android设备的最佳 HiSuite 替代品
HiSuite 是由华为开发的一款 PC 套件,旨在通过 USB 或 Wi-Fi 管理华为手机。虽然它与华为设备配合良好,但也存在一些局限性,例如不支持其他Android品牌、云集成程度低以及偶尔出现的兼容性问题。如果您正在寻找功能更丰富或更人性化的方案&am…...
AI推理模型工程2026:从o3到DeepSeek-R1的工程化落地实践
推理模型(Reasoning Model)正在重新定义AI应用的边界。当OpenAI o3在ARC-AGI测试上突破人类基准,当DeepSeek-R1以极低成本实现顶级推理能力,工程师们面临的问题已经不是"推理模型能做什么",而是"怎么把…...
Midjourney啤酒瓶身3D贴图生成术:1个命令实现曲面延展+光影自适应(含GitHub开源LUT校色包)
更多请点击: https://intelliparadigm.com 第一章:Midjourney Beer印相 Midjourney Beer印相并非官方功能,而是社区开发者基于 Midjourney API 与啤酒文化融合的创意实验项目——通过文本提示(prompt)生成具有精酿啤酒…...
学校RFID借阅柜源头生产厂家推荐
在学校智慧图书馆的建设版图中,RFID借阅柜扮演着“毛细血管”的角色,它将图书馆的服务触角延伸至教学楼的走廊、学生宿舍的大厅乃至教师办公室。这种分布式的借阅模式,极大地提升了图书资源的可达性与利用率。然而,分布式部署对设…...
终极指南:如何在Windows上安装APK文件?3分钟学会跨平台应用安装
终极指南:如何在Windows上安装APK文件?3分钟学会跨平台应用安装 【免费下载链接】APK-Installer An Android Application Installer for Windows 项目地址: https://gitcode.com/GitHub_Trending/ap/APK-Installer 你是否想在Windows电脑上直接运…...
