“我要做太空人”----太空人表盘制作

786 阅读4分钟

“这是我参与更文挑战的第4天,活动详情查看: 更文挑战”

需求

有智能手表的人都知道,前段时间的“太空人表盘”非常火热。那么我们也可以自己动手来制作一款太空人表盘。

准备

  1. Visual Studio 2019
  2. EasyX Graphics Library :Visual C++ 的免费绘图库

程序

huawei.h

#ifndef _HUAWEI_H_
#define _HUAWEI_H_
#include<graphics.h>
void SetWindowNewStyle(int w, int h)
{
	// 去掉标题
	SetWindowLong(GetHWnd(), GWL_STYLE, GetWindowLong(GetHWnd(), GWL_STYLE) & ~WS_CAPTION);
	// 初始化界面为圆形
	SetWindowRgn(GetHWnd(), CreateEllipticRgn(0, 0, w, h), true);
}
//png透明贴图
void drawImg(IMAGE* pimg, int x, int y)
{
	// 变量初始化
	DWORD* dst = GetImageBuffer();
	DWORD* src = GetImageBuffer(pimg);
	int dst_width = ::getwidth();			//窗口宽高
	int dst_height = ::getheight();
	int _w = pimg->getwidth();
	int _h = pimg->getheight();

	// 计算贴图的实际长宽
	int iwidth = (x + _w > dst_width) ? dst_width - x : _w;		// 处理超出右边界
	int iheight = (y + _h > dst_height) ? dst_height - y : _h;	// 处理超出下边界
	if (x < 0) { src += -x;				iwidth -= -x;	x = 0; }				// 处理超出左边界
	if (y < 0) { src += (_w * -y);	iheight -= -y;	y = 0; }				// 处理超出上边界
																		// 修正贴图起始位置
	dst += (dst_width * y + x);

	// 实现透明贴图
	for (int iy = 0; iy < iheight; iy++)
	{
		for (int ix = 0; ix < iwidth; ix++)
		{
			byte a = (byte)(src[ix] >> 24);//计算透明通道的值[0,256) 0为完全透明 255为完全不透明
			if (a > 100)
			{
				dst[ix] = src[ix];
			}
		}
		//换到下一行
		dst += dst_width;
		src += _w;
	}
}
//去掉窗口标题以后,能够点击移动窗口
void mouseEvent()
{
	//求鼠标相对于当前窗口左上角的坐标(即鼠标距离窗口左上的的宽度和高度)
	static POINT WndPtSize;
	static bool isMove = false;
	if (MouseHit())
	{
		MOUSEMSG msg = GetMouseMsg();
		if (msg.uMsg == WM_LBUTTONDOWN)	//左键按下
		{
			//获取窗口相对与屏幕左上角的 左上角坐标,和右下角坐标
			RECT wndRect;
			GetWindowRect(GetHWnd(), &wndRect);


			//获取鼠标光标相对于屏幕的坐标
			POINT curPos;
			GetCursorPos(&curPos);

			//求鼠标相对于当前窗口左上角的坐标
			WndPtSize.x = curPos.x - wndRect.left;
			WndPtSize.y = curPos.y - wndRect.top;

			isMove = true;
		}
		else if (msg.uMsg == WM_LBUTTONUP)//左键弹起
		{
			isMove = false;
		}
		else if (msg.uMsg == WM_MOUSEMOVE)//鼠标移动
		{
			if (isMove)
			{
				POINT CursorPos;
				GetCursorPos(&CursorPos);
				/*把窗口移动到屏幕的x,y的位置
				* @hwnd:窗口句柄
				* @hwndInsertAfter:窗口的z顺序  HWND_TOPMOST {在前面, 位于任何顶部窗口的前面}
				* @X,Y: 窗口左上角的新位置(相对于屏幕)
				* @cx,xy: 窗口大小
				* @uFlags:SWP_NOSIZE {忽略 cx、cy, 保持大小}
				*/
				SetWindowPos(GetHWnd(), HWND_TOPMOST, CursorPos.x - WndPtSize.x, CursorPos.y - WndPtSize.y, 0, 0, SWP_NOSIZE);
				//CursorPos.x - WndPtSize.x	//获取当前窗口左上角相对于屏幕的坐标
				//CursorPos.y - WndPtSize.y

				//printf("%d %d\n", CursorPos.x - WndPtSize.x, CursorPos.y - WndPtSize.y);
			}
		}
		else if (msg.uMsg == WM_RBUTTONDOWN)//右键按下
		{
			exit(0);
		}
	}
}
#endif // !_TOOLS_H_


huawei.c

#include <stdio.h>
#include <time.h>
#include "huawei.h"
#include <mmsystem.h>
#pragma comment(lib,"winmm.lib")
#pragma warning(disable:4996)
/*
1,文本字体设置
2,图片动画效果
3,绘制表盘
*/
#define WIN_SIZE 500
#define WIN_HALF (WIN_SIZE/2)	//窗口的一半
IMAGE spaceMan[59];
IMAGE other[6];
const char *week[7] = { "日","一","二","三","四","五","六" };
void setTextStyle(int height, int width, const char *faceName)
{
	LOGFONT f = { 0 };
	f.lfHeight = height;
	f.lfWidth = width;
	f.lfQuality = ANTIALIASED_QUALITY;
	strcpy(f.lfFaceName, faceName);
	settextstyle(&f);
}
void loadImg()
{
	mciSendString("D:\\VS\\作品\\智能表盘\\images/风儿吹.mp3", NULL, 0, NULL);
	mciSendString("D:\\VS\\作品\\智能表盘\\images/风儿吹.mp3 repeat", NULL, 0, NULL);

	char fileName[50] = { 0 };
	for (int i = 0; i < 30; i++)
	{
		sprintf_s(fileName, "./images/guoguoxiaoshidi (%d).jpeg", i + 1);
		loadimage(spaceMan + i, fileName, 140, 130);
	}

	loadimage(&other[0], "./images/xinlv.jpg", 60, 60);//心率
	loadimage(&other[1], "./images/sun.jpg", 40, 40);//太阳
	loadimage(&other[2], "./images/shoes.jpg", 40, 40);//鞋子
	loadimage(&other[3], "./images/shang.jpg", 30, 30);//上箭头
	loadimage(&other[4], "./images/xia.jpg", 30, 30);//下箭头
	loadimage(&other[5], "./images/rocket.jpg", 40, 40);//火箭
}

//太空人旋转动画
void animation()
{
	static int index = 0;	//[0~59)
	putimage(175, 210, spaceMan + index);
	static DWORD t1;
	DWORD t2 = clock();//获取程序运行到调用该函数经过的毫秒
	if (t2 - t1 > 20)
	{
		index = (index + 1) % 30;
		t1 = t2;
	}
}
void gameDraw()
{
	setbkcolor(RGB(255, 0, 0));
	cleardevice();
	//绘制表盘
	setlinecolor(RGB(0, 0, 0));//设置边框颜色
	setlinestyle(PS_SOLID, 30);
	setfillcolor(RGB(255, 255, 255));//设置圆的填充白色
	fillellipse(0, 0, WIN_SIZE, WIN_SIZE);//绘制一个圆

	//绘制线条
	setlinestyle(PS_SOLID, 4);
	setlinecolor(BLACK);
	//最上面竖线
	line(WIN_HALF - 30, 20, WIN_HALF - 30, 130);
	//横线x2
	line(WIN_HALF - 195, WIN_HALF - 120, WIN_HALF + 195, WIN_HALF - 120);
	line(WIN_HALF - 195, WIN_HALF + 120, WIN_HALF + 195, WIN_HALF + 120);
	//下面线条x3
	line(WIN_HALF + 80, WIN_HALF + 120, WIN_HALF + 80, WIN_HALF + 175);
	line(WIN_HALF + 80, WIN_HALF + 175, WIN_HALF - 60, WIN_HALF + 175);
	line(WIN_HALF - 60, WIN_HALF + 175, WIN_HALF - 60, WIN_HALF + 175 + 48);
	setbkmode(TRANSPARENT);
	//左上空气湿度90%
	setTextStyle(55, 23, "Arial");
	settextcolor(BLACK);
	outtextxy(WIN_HALF - 155, 75, "90%");
	drawImg(other + 5, WIN_HALF - 90, 35);	//火箭											//右上
	putimage(WIN_HALF - 90, 35, other + 5);
	setTextStyle(25, 15, "黑体");
	outtextxy(WIN_HALF - 25, 35, "空气良好");
	setTextStyle(25, 13, "宋体");
	outtextxy(WIN_HALF - 25, 65, "晴天");
	outtextxy(WIN_HALF - 25, 95, "25℃");
	outtextxy(WIN_HALF + 38, 65, "26°");
	outtextxy(WIN_HALF + 38, 95, "17°");
	drawImg(other + 4, WIN_HALF + 73, 60);	//上面的箭头
	drawImg(other + 3, WIN_HALF + 73, 90);	//下面的箭头
	drawImg(other + 1, WIN_HALF + 105, 70);	//太阳
	putimage(WIN_HALF + 73, 60, other + 4);
	putimage(WIN_HALF + 73, 90, other + 3);
	putimage(WIN_HALF + 105, 70, other + 1);
	// 下部分
	setTextStyle(37, 17, "宋体");
	outtextxy(100, WIN_HALF + 130, "睡眠");
	outtextxy(WIN_HALF + 90, WIN_HALF + 130, "距离");
	outtextxy(50, WIN_HALF-40, "平顶山");

	setTextStyle(40, 15, "Arial");
	outtextxy(185, WIN_HALF + 125, "7h30m");
	outtextxy(215, WIN_HALF + 180, "9.88km");
	//中间
	//心率
	setTextStyle(25, 13, "宋体");
	outtextxy(60, WIN_HALF + 30, "80~128");
	drawImg(&other[0], 65, WIN_HALF + 50);	//心率图
	putimage(65, WIN_HALF + 50, other + 0);
	setTextStyle(40, 15, "Arial");
	outtextxy(135, WIN_HALF + 60, "92");
	// 步数
	drawImg(&other[2], WIN_HALF + 65, WIN_HALF + 65);
	putimage(WIN_HALF + 65, WIN_HALF + 65, &other[2]);
	outtextxy(WIN_HALF + 125, WIN_HALF + 75, "9527");
	//时间、日期相关
	time_t timep = time(NULL);			//获取当前时间
	struct tm* p = localtime(&timep);	//把时间转成格式化时间
	setTextStyle(25, 12, "宋体");
	outtextxy(WIN_HALF + 110, WIN_HALF - 20, "四月六号");
	char fileName[40] = { 0 };
	sprintf_s(fileName, "周%s %d-%d", week[p->tm_wday], p->tm_mon + 1, p->tm_mday);
	outtextxy(WIN_HALF + 110, WIN_HALF + 10, fileName);
	// 获取字体
	setTextStyle(100, 40, "Arial");
	char szBuf[40] = { 0 };
	sprintf_s(szBuf, "%d:%02d", p->tm_hour, p->tm_min);
	outtextxy(105, 120, szBuf);
	// 秒
	setTextStyle(55, 23, "Arial");
	sprintf(szBuf, "%02d", p->tm_sec);
	outtextxy(335, 160, szBuf);
}
int main()
{
	initgraph(WIN_SIZE, WIN_SIZE/*,EW_SHOWCONSOLE*/);
	SetWindowNewStyle(WIN_SIZE, WIN_SIZE);
	loadImg();
	BeginBatchDraw();//双缓冲 防止闪屏
	while (true)
	{
		gameDraw();
		animation();
		mouseEvent();
		FlushBatchDraw();
	}
	EndBatchDraw();
	return 0;
}

运行图

后续

如果想了解更多物联网知识,可以关注我的项目实战专栏