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…...
DAY 47
三、通道注意力 3.1 通道注意力的定义 # 新增:通道注意力模块(SE模块) class ChannelAttention(nn.Module):"""通道注意力模块(Squeeze-and-Excitation)"""def __init__(self, in_channels, reduction_rat…...
Cloudflare 从 Nginx 到 Pingora:性能、效率与安全的全面升级
在互联网的快速发展中,高性能、高效率和高安全性的网络服务成为了各大互联网基础设施提供商的核心追求。Cloudflare 作为全球领先的互联网安全和基础设施公司,近期做出了一个重大技术决策:弃用长期使用的 Nginx,转而采用其内部开发…...
04-初识css
一、css样式引入 1.1.内部样式 <div style"width: 100px;"></div>1.2.外部样式 1.2.1.外部样式1 <style>.aa {width: 100px;} </style> <div class"aa"></div>1.2.2.外部样式2 <!-- rel内表面引入的是style样…...
Web 架构之 CDN 加速原理与落地实践
文章目录 一、思维导图二、正文内容(一)CDN 基础概念1. 定义2. 组成部分 (二)CDN 加速原理1. 请求路由2. 内容缓存3. 内容更新 (三)CDN 落地实践1. 选择 CDN 服务商2. 配置 CDN3. 集成到 Web 架构 …...
【数据分析】R版IntelliGenes用于生物标志物发现的可解释机器学习
禁止商业或二改转载,仅供自学使用,侵权必究,如需截取部分内容请后台联系作者! 文章目录 介绍流程步骤1. 输入数据2. 特征选择3. 模型训练4. I-Genes 评分计算5. 输出结果 IntelliGenesR 安装包1. 特征选择2. 模型训练和评估3. I-Genes 评分计…...
【C++特殊工具与技术】优化内存分配(一):C++中的内存分配
目录 一、C 内存的基本概念 1.1 内存的物理与逻辑结构 1.2 C 程序的内存区域划分 二、栈内存分配 2.1 栈内存的特点 2.2 栈内存分配示例 三、堆内存分配 3.1 new和delete操作符 4.2 内存泄漏与悬空指针问题 4.3 new和delete的重载 四、智能指针…...
C#学习第29天:表达式树(Expression Trees)
目录 什么是表达式树? 核心概念 1.表达式树的构建 2. 表达式树与Lambda表达式 3.解析和访问表达式树 4.动态条件查询 表达式树的优势 1.动态构建查询 2.LINQ 提供程序支持: 3.性能优化 4.元数据处理 5.代码转换和重写 适用场景 代码复杂性…...
4. TypeScript 类型推断与类型组合
一、类型推断 (一) 什么是类型推断 TypeScript 的类型推断会根据变量、函数返回值、对象和数组的赋值和使用方式,自动确定它们的类型。 这一特性减少了显式类型注解的需要,在保持类型安全的同时简化了代码。通过分析上下文和初始值,TypeSc…...
Vite中定义@软链接
在webpack中可以直接通过符号表示src路径,但是vite中默认不可以。 如何实现: vite中提供了resolve.alias:通过别名在指向一个具体的路径 在vite.config.js中 import { join } from pathexport default defineConfig({plugins: [vue()],//…...
Leetcode33( 搜索旋转排序数组)
题目表述 整数数组 nums 按升序排列,数组中的值 互不相同 。 在传递给函数之前,nums 在预先未知的某个下标 k(0 < k < nums.length)上进行了 旋转,使数组变为 [nums[k], nums[k1], …, nums[n-1], nums[0], nu…...
