罗盘时钟
完美复刻抖音上的罗盘时钟。~~~
#include <stdio.h>
#include <graphics.h>
#include <math.h>
#include <time.h>
#include <string.h>
#define windwidth 640 //窗口宽度
#define windhigh 480 //窗口高度
#define pai 3.1415926 //定义π的值
#define MAXSIZE 10 //定义文字长度
#define MAXNUM 60 //定义分钟和秒的数组最大长度
/*此处用sin函数实现初始化时分秒表盘的变速展开,y=sin(0到180),
y值的和为展开的速度;也可以用抛物线方程实现*/
#define Ch 1.909084 //每45度sin值的和与15度的比值,用于实现时针变速展开;360/24=15
#define Cm 3.180190 //每30度sin值的和与6度的比值,用于实现分针变速展开;360/60=6
#define Cs 2.117701 //每20度sin值的和与6度的比值,用于实现秒针的变速展开;
#define eps 1e-8 //定义浮点数比较精度,用于比较两个浮点数是否相等
#define Equ(a,b) fabs((a)-(b))<(eps)//定义判断浮点数是否相等的宏
typedef struct _node{ //声明表盘结构体
char time[MAXSIZE]; //存放需要输出汉字时间的字符串
double x; //输出汉字时间的水平位置
double y; //输出汉字时间的垂直位置
double angle; //当下时间的角度,x轴正方向为0(或者为360),顺时针递增
int c; //输出位置距离原点的距离
}Node;
Node sec[MAXNUM]; //定义秒针数组
Node min[MAXNUM]; //定义分针数组
Node hou[MAXNUM]; //定义时针数组
/*该函数返回180度内sin值的和与S的比值,参数S为需要旋转的角度,用于实现变速旋转*/
double C_sin(double S){
double f=0,sum=0;
for(int i=0;i<=20;i++){ //20控制旋转速度
f=sin(pai/180*i*9);
sum+=f;
}
return sum/S;
}
/*初始化表盘结构体*/
void init_time(){
char num[][MAXSIZE]={"零","一","二","三","四","五","六","七","八","九","十","十一","十二",\
"十三","十四","十五","十六","十七","十八","十九","二十","二十一","二十二","二十三",\
"二十四","二十五","二十六","二十七","二十八","二十九","三十","三十一","三十二","三十三",\
"三十四","三十五","三十六","三十七","三十八","三十九","四十","四十一","四十二","四十三","四十四",\
"四十五","四十六","四十七","四十八","四十九","五十","五十一","五十二","五十三","五十四","五十五",\
"五十六","五十七","五十八","五十九"};
for(int i=0;i<MAXNUM;i++){
strcpy(sec[i].time,num[i]);
strcat(sec[i].time,"秒");
sec[i].c=windhigh/2+40;
sec[i].x=windhigh/2+40+windwidth/2;
sec[i].y=windhigh/2;
sec[i].angle=360;
strcpy(min[i].time,num[i]);
strcat(min[i].time,"分");
min[i].c=windhigh/2-40;
min[i].x=windhigh/2-40+windwidth/2;
min[i].y=windhigh/2;
min[i].angle=360;
if(i<24){
strcpy(hou[i].time,num[i]);
strcat(hou[i].time,"时");
hou[i].c=windhigh/2-120;
hou[i].x=windhigh/2-120+windwidth/2;
hou[i].y=windhigh/2;
hou[i].angle=360;
}
}
}
/*初始化表盘位置,实现展开过程*/
void init_hand(int cnt){ //cnt为调用此函数的次数
static double h=0,m=0,s=0; //定义静态变量,h、m、s分别控制时分秒表盘展开速度
if(cnt<=45){ //调用45次,时针展开完成,45次从0-180.每次变化4度
h+=sin(pai/180*cnt*4); //实现h值的变速增加,最终h/Ch=15,为两个相邻小时的间隔度数
for(int i=0;i<MAXNUM-1;i++){ //从0时到23时,间隔逐渐增加至15.
hou[i+1].angle=hou[i].angle-h/Ch;
}
}
if(cnt>=30&&cnt<=60){ //调用30次,分针展开完成,30次从0-180,每次变化6度
m+=sin(pai/180*(cnt-30)*6); //实现m值的变速增加,最终m/Cm=6,为两个相邻分钟的间隔度数
for(int i=1;i<MAXNUM;i++){ //从0分到59分,间隔逐渐增加至6。
min[i].angle=min[i-1].angle-m/Cm;
}
}
if(cnt>=50&&cnt<=70){ //调用20次,秒针展开完成,20次从0-180,每次变化9度
s+=sin(pai/180*(cnt-50)*9); //实现s值的变速增加,最终s/Cs=6,为两个相邻秒的间隔度数
for(int i=1;i<MAXNUM;i++){ //从0秒到59秒,间隔逐渐增加至6.
sec[i].angle=sec[i-1].angle-s/Cs;
}
}
}
/*实现表盘顺时针旋转*/
void spin_hand(struct tm *t){
int s=t->tm_sec,m=t->tm_min,h=t->tm_hour;
static int flag=0,n=0;
if(flag==0){ //对表,cs、cm、ch为sin值的和与旋转角度的比值
static double cs=C_sin(360-sec[s].angle);
static double cm=C_sin(360-min[m].angle);
static double ch=C_sin(360-hou[h].angle);
double vs=sin(pai/180*n*9)/cs; //秒针旋转速度
double vm=sin(pai/180*n*9)/cm; //分针旋转速度
double vh=sin(pai/180*n*9)/ch; //时针旋转速度
if(n<=20){ //调用20次完成对时
n++;
for(int i=0;i<MAXNUM;i++){
sec[i].angle+=vs;
min[i].angle+=vm;
hou[i].angle+=vh;
}
}else{
flag=1; //标记完成对时
}
}
if(flag){ //进入正常时间宣传速度
static int dx=windwidth/2,dy=windhigh/2+17;
line(windwidth/2,windhigh/2+17,dx,dy);
if(dx<windwidth){ //实现划线的动态效果
dx+=80;
}else{
for(int i=0,k=360;i<MAXNUM;i++,k-=6,s++,m++){
sec[s%60].angle=k; //s为此时刻秒数,s%60保证了sec[s%60]不会越界,变化范围0-59。
min[m%60].angle=k; //同上
}
for(int i=0,k=360;i<24;i++,h++,k-=15){
hou[h%24].angle=k; //继续同上,不过k的变化幅度每次15度
}
}
}
}
/*画表盘*/
void draw_hand(){
setcolor(EGEARGB(0XFF,255,255,255));
setbkmode(TRANSPARENT);
LOGFONTW font;
getfont(&font);
font.lfHeight=16;
for(int i=0;i<60;i++){
if(i<24){
font.lfEscapement=-hou[i].angle/0.1; //字体的角度默认是前端翘起为正角度,单位0.1
setfont(&font);
hou[i].x=hou[i].c*cos(hou[i].angle*pai/180)+windwidth/2+0.5;//浮点数末位+0.5实现四舍五入
hou[i].y=hou[i].c*sin(hou[i].angle*pai/180)+windhigh/2+0.5;
settextjustify(RIGHT_TEXT,TOP_TEXT);
xyprintf(hou[i].x,hou[i].y,hou[i].time);
}
font.lfEscapement=-min[i].angle/0.1;
setfont(&font);
min[i].x=min[i].c*cos(min[i].angle*pai/180)+windwidth/2+0.5;
min[i].y=min[i].c*sin(min[i].angle*pai/180)+windhigh/2+0.5;
xyprintf((int)min[i].x,(int)min[i].y,min[i].time);
font.lfEscapement=-sec[i].angle/0.1;
setfont(&font);
sec[i].x=sec[i].c*cos(1.0*sec[i].angle*pai/180)+windwidth/2+0.5;
sec[i].y=sec[i].c*sin(1.0*sec[i].angle*pai/180)+windhigh/2+0.5;
xyprintf((int)sec[i].x,(int)sec[i].y,sec[i].time);
}
}
void draw_bk(PIMAGE bkimg,struct tm *now){
putimage(0,0,bkimg);
setcolor(EGEARGB(0XFF,255,255,255));
setbkmode(TRANSPARENT);
setfont(16,0,"仿宋");
settextjustify(CENTER_TEXT,TOP_TEXT);
xyprintf(windwidth/2,windhigh/2,"%d年%d月%d日",now->tm_year+1900,now->tm_mon+1,now->tm_mday);
}
int main(int argc,char const *argv[]){
//初始化窗口
setinitmode(INIT_ANIMATION,200,200);
setcaption("时钟");
initgraph(windwidth,windhigh);
//设置背景图
PIMAGE bkimg=newimage(640,480);
getimage(bkimg,"2.jpg",0,0);
init_time();
//EGE高级画图,抗锯齿
ege_enable_aa(true);
//主要消息循环
for(int cnt=0;is_run();delay_fps(60)){
time_t t; //定义一个time_t类型的变量t,其实也就是long int类型
time(&t); //time函数给t赋值,为1970年1月1日至此刻的秒数。
struct tm *now=localtime(&t); //定义一个struct tm 类型的结构体指针,指向localtime函数返回的此刻时间
// char *timeNow=ctime(&t); //ctime函数将t转换为表达此刻时间的字符串
cleardevice(); //清屏
draw_bk(bkimg,now);
if(cnt<=90){
init_hand(cnt);
cnt++;
}
else{
spin_hand(now);
}
draw_hand();
}
delimage(bkimg); //删除背景图片内存,防止内存泄露
closegraph();
return 0;
}