第一部分:电子相册,界面及功能展示如下
1.1交互互动高亮显示
1.2点击出现方框,方框内小图对应的大图预览
1.3点击箭头,小图边框向下切换,同时大图显示切换
1.4背景音乐
第二部分:项目分析
2.1ToDO清单:
2.1.1实现基本界面
2.1.2两个按钮绘制及功能实现
①按钮功能,鼠标进入黄色高亮
② 点击一下,出现矩形方框并切换
③点击,方框内的小图与切换的大图同步显示
2.1.3加载音乐
2.2难点:如何让小图和大图同步,以及点击翻页逻辑实现
2.3需要提前掌握的C基础知识
①数组
②函数的声明,调用及全局变量
③for,while循环语句
④if,else-if, switch选择语句
2.4需要额外掌握的技能分析
①窗口可视化编程
②贴图
2.5项目流程:
《1》 加载资源
《2》 画按钮
《3》 交互处理①(进入高亮,点击按钮事件,小图显示方框)
《4》 交互处理②-小图显示的方框切换对应大图片
第三部分:项目代码实战
3.1 《加载资源》
3.1.1基本可视化编程教程——利用graphcis.h库的initgraph()函数,比如创建一个800*600大小的窗口
/****************************
具备能力:
1.基本可视化编程
1.1 initgraph(800,600); 比如创建一个800*600的窗口
1.2 关闭窗口 closegraph()
***************************/
#include<stdio.h> //基本库
#include<graphics.h> //图形库
int main()
{
initgraph(800, 600); //创建一个800*600的窗口
getchar();//输入等待,不然窗口一闪而过,
//或者可以使用死循环
//while(1);//代替
closegraph();//一般程序退出之前关闭窗口
return 0;
}
执行如下图
如果没有graphics.h库文件编译可能会报错,可以通过EasyX插件安装
3.1.2贴图(三步曲,创建变量,加载,显示)
#include<stdio.h>
#include<graphics.h>
int main()
{
initgraph(932, 538); // 根据素材像素大小创建同样大小窗口
IMAGE backImg;//第一步,创建一个名为backImg的背景图变量
loadimage(&backImg, "jiemian.jpg"); //第二步,加载界面背景框,注意资源文件路径为相对路径,为当前工作目录。注意&,传入变量名的地址
//loadimage(&backImg, "jiemian.jpg",600,400);//缩放的形式,缩放到600,400
putimage(0, 0, &backImg);//第三步,在坐标有0,0处,放置图片
getchar();
closegraph();
return 0;
}
Tips: 素材拷入工程目录下(VC++6.0)
或者是vs2015
执行效果如下
3.1.3贴图功能封装为drawMap()函数(思考一下,为什么要封装函数)
#include<stdio.h>
#include<graphics.h>
/*****************************************
* 初始化界面
* 画界面, 画背景,画按钮, 画文字
* drawMap();
******************************************/
void drawMap()
{
IMAGE backImg,smallCar0,smallCar1,bigCar0; // 第一步,创建IMAGE类型变量,4个
//第二步,定义图片加载的方式
loadimage(&backImg,"jiemian.jpg");
loadimage(&smallCar0,"0.jpg",150,160);
loadimage(&smallCar1,"1.jpg",150,160);
loadimage(&bigCar0,"0.jpg",500,400);
//第三步,在具体坐标显示图片
putimage(0, 0, &backImg);
putimage(80, 130, &smallCar0); //具体位置,用截图工具去测量,需要在哪里画 记住大致的位置 取整数即可
putimage(80, 330, &smallCar1); //位置计算, 加上每一张小图的大小, 160+130=290,稍微大一点到310
putimage(345,100,&bigCar0);
}
---
封装为函数之后,不调用是不会有效果的
```c
int main()
{
initgraph(932, 538); // 根据素材像素大小创建同样大小窗口
drawMap(); //封装的函数一定要记得在main里面调用,绘制界面
getchar();
closegraph();
return 0;
}
观察上面的代码会发现,我们把基础图片的功能做出来了。但是这个功能封装完之后不完善:
①只显示了第0张和第1张照片
②代码冗余,如果有8张照片,我们的IMAGE变量要有8个,loadimage()函数和putimage()函数就要放8个
解决办法
封装一个加载资源的函数loadResource(),复用loadimage()函数, 同时将变量图片作为数组保存下来
3.1.4封装loadResouce()函数,加载资源
部分代码如下
```c
//全局变量
IMAGE backImg; //存放背景变量
IMAGE carBig[8];//存放大车
IMAGE carSmall[8];//存放小车
/*****************************************
* 初始化资源
* 封装一个函数,初始化数据:初始化变量
* loadResource(); 没有返回值
******************************************/
void loadResource()
{
loadimage(&backImg, "jiemian.jpg");
//笨方法写
loadimage(carSmall+0, "0.jpg",150, 160); //注意这里,数组名就是地址,不需要对变量名取地址了
loadimage(carSmall+1, "1.jpg",150, 160);
loadimage(carSmall+2, "2.jpg",150, 160);
loadimage(carSmall+3, "3.jpg",150, 160);
loadimage(carSmall+4, "4.jpg",150, 160); //注意这里,数组名就是地址,不需要对变量名取地址了
loadimage(carSmall+5, "5.jpg",150, 160);
loadimage(carSmall+6, "6.jpg",150, 160);
loadimage(carSmall+7, "7.jpg",150, 160);
loadimage(carBig+0,"0.jpg", 500, 400); //注意这里,数组名就是地址,不需要对变量名取地址了
loadimage(carBig+1,"1.jpg", 500, 400);
loadimage(carBig+2,"2.jpg", 500, 400);
loadimage(carBig+3,"3.jpg", 500, 400);
loadimage(carBig+4,"4.jpg", 500, 400); //注意这里,数组名就是地址,不需要对变量名取地址了
loadimage(carBig+5,"5.jpg", 500, 400);
loadimage(carBig+6,"6.jpg", 500, 400);
loadimage(carBig+7,"7.jpg", 500, 400);
}
/*****************************************
* 初始化界面
* 画界面, 画背景,画按钮, 画文字
* drawMap();
******************************************/
void drawMap()
{
//画背景
putimage(0, 0, &backImg); //传入地址 ,后面是carSmall数组名本身就是地址
//画小图 第0张和第1张
putimage(80, 130, carSmall + 0); //具体位置,用截图工具去测量,需要在哪里画 记住大致的位置 取整数即可
putimage(80, 330, carSmall + 1); //位置计算, 加上每一张小图的大小, 160+130=290,稍微大一点到310
//画大图
putimage(345,100,carBig+0);
}
同时要注意,main()函数中要调用刚刚封装的函数
int main()
{
initgraph(932, 538); // 根据素材像素大小创建同样大小窗口
loadResource(); //依然要记得调用刚刚的函数,加载整个资源
drawMap();
getchar();
closegraph();
return 0;
}
①观察loadResource()代码,发现这是一个笨方法,我们只是实现了图片以不同大小的方式穷举加载
②简化loadResource()代码,我们就需要循环,但是循环中,路径是不断的变化的,我们传递进去的图片名是0开头,7结尾的数字
③用for循环中的i变量 替代 0,1,2,3,4,5,6,7这些数字
④思考一下,我们可以简单的 loadimage(carBig+i,"i.jpg",500,400)和 loadimage(carsmall+i,"i.jpg",150,160)吗?
该库中的函数,并没有这种传递i的功能
介绍一个新的系统函数 sprintf(),把路径在for循环里面加上i的变化,以写入字符串的方式来加载
3.1.5循环处理loadResource()函数代码
void loadResource()
{
loadimage(&backImg, "jiemian.jpg");
//for循环批量加载:因为文件名有规律格式 从0.jpg到7.jpg
for (int i = 0; i <= 7; i++)
{
char fileName[20] = "";
// 0.jpg 1.jpg 2.jpg .....
sprintf(fileName, "%d.jpg", i); //把文件名 *.jpg 这个作为路径名写入数组, 那么循环8次,就初始化了8次
loadimage(carBig + i, fileName, 500, 400);
loadimage(carSmall + i, fileName, 150, 160);
}
}
Tips:使用sprintf()函数,VS2015会需要在最开头加上一个宏定义#define _CRT_SECURE_NO_WARNINGS
避免安全检查错误
drawMap()函数代码不变,main函数()代码不变
3.2《画按钮》
3.2.1按钮是一个填充圆,内嵌一个空心圆,以及箭头。那么我们就需要掌握,画圆和画线的函数
①solidcircle(x,y,r) 实心圆及填充颜色参数设置 setfillcolor(BLACK)
②circle(x,y,r)空心圆
③line(x,y,xx,yy)两点确定一条线,以及设置线型setlinestyle(PS_SOLID,pixel_width,0)及线颜色setlinecolor(BLACK)
//此段代码复制进入drawButton()函数
setfillcolor(BLACK);// 设置填充颜色为黑色
solidcircle(50, 50, 20);//在x,y的位置画一个半径为20的圆
setlinecolor(BLACK);//设置线的颜色为白色
setlinestyle(PS_SOLID, 3, 0);//设置线的格式, 实线,3个像素宽,0为自适应
line(50,60,100,60);
画按钮的功能比较复杂,所以我们要封装进一个函数,以后再遇到需要画按钮的地方,直接复制粘贴就可以了
3.2.2drawButton()函数封装
问题分析,画2个按钮,需要在相应的坐标放置,同时是左右两个按钮,可以通过传递按钮标记,实现不同按钮的箭头方向绘制
/*****************************************
* 画按钮
* drawButton();
* 有三个参数,坐标及左右按钮标记
* 画线函数 line(int x,int y,int xx,int yy) 两点确定一条线,三条线画一个箭头
* 画圆,实心圆solidcircle(x,y,R),空心圆 circle(x,y,R)
* 实心圆填充颜色 setfillcolor(),空心圆线条颜色 setlinecolor()
******************************************/
void drawButton(int x,int y, char postion) //x,y传递进来按钮的坐标, postion传递朝向标记 <--- l ---> r
{
setfillcolor(BLACK);// 设置填充颜色为黑色
solidcircle(x, y, 20);//在x,y的位置画一个半径为20的圆
setlinecolor(WHITE);//设置线的颜色为白色
setlinestyle(PS_SOLID, 3, 0);//设置线的格式, 实线,3个像素宽,0为自适应
if (postion == 'r')
{
circle(x, y, 15);//圆中圆,半径比20小一点,定位15
}
else if (postion == 'l') //左按钮的箭头及圆待完善
{
}
}
现在,右边的按钮实现了一半的功能,我们边写代码 变调试,画画看样子,删除main()中关于画圆及线的代码
并且在drawMap()函数里面,调用drawButton(),记得传递要绘制按钮的坐标 x,y,以及按钮标记r或者l
void drawMap()
{
//画背景
putimage(0, 0, &backImg);
//画小图 第0张和第1张
putimage(80, 130, carSmall + 0); //具体位置,用截图工具去测量,需要在哪里画 记住大致的位置 取整数即可
putimage(80, 330, carSmall + 1); //位置计算, 加上每一张小图的大小, 160+130=290,稍微大一点到310
//画大图
putimage(345,100,carBig+0);
drawButton(180, 120, 'r'); //传递坐标进来
}
int main()
{
initgraph(932, 538); // 根据素材像素大小创建同样大小窗口
loadResource(); //依然要记得调用刚刚的函数
drawMap();
getchar();
closegraph();
return 0;
}
如下图所示
然后在drawMap()里面调用drawButton()函数,通过传递不同的x,y值进去,略微调整一下按钮的位置
void drawMap()
{
//画背景
putimage(0, 0, &backImg);
//画小图 第0张和第1张
putimage(80, 130, carSmall + 0); //具体位置,用截图工具去测量,需要在哪里画 记住大致的位置 取整数即可
putimage(80, 330, carSmall + 1); //位置计算, 加上每一张小图的大小, 160+130=290,稍微大一点到310
//画大图
putimage(345,100,carBig+0);
//画按钮比较复杂,需要封装函数
// 按钮边画边试
drawButton(200, 90, 'r'); //画右边按钮
}
位置差不多了,我们来把drawButton()函数中的箭头功能补齐,这个箭头实际上就是三根实线:一根横线以及向上和向下的两根斜线
依然是边画边试,keep in mind,前面我们传递进来的x,y坐标确定了按钮的绝对位置,画线就是根据圆心坐标 x,y的相对位置的坐标来画线段
void drawButton(int x,int y, char postion) //按钮的坐标, 和朝向标记 <--- l ---> r
{
setfillcolor(BLACK);// 设置填充颜色为黑色
solidcircle(x, y, 20);//在x,y的位置画一个半径为20的圆
setlinecolor(WHITE);//设置线的颜色为白色
setlinestyle(PS_SOLID, 3, 0);//设置线的格式, 实线,3个像素宽,0为自适应
if (postion == 'r')
{
circle(x, y, 15);//圆中圆
//画线,做简单的计算,圆心坐标x,y定位,左右减加5
line(x - 10, y, x + 10,y);//画横线
line(x - 3,y - 5,x + 10,y);//画箭头上横线, 从一个点画到 x+10,y这个点,调试 前面2个参数到合适位置
line(x - 3, y + 5, x + 10, y);//画箭头下横线,对称处理
}
else if (postion == 'l')
{
}
}
按钮美化的工作,可以自己按审美完成,调整x±数值与y±数值即可
我们来补齐左边按钮,以及在drawMap()函数里面把左边按钮传递进去给绘制出来,并且在大图上方空白处显示相应的文字
void drawButton(int x,int y, char postion) //按钮的坐标, 和朝向标记 <--- l ---> r
{
setfillcolor(BLACK);// 设置填充颜色为黑色
solidcircle(x, y, 20);//在x,y的位置画一个半径为20的圆
setlinecolor(WHITE);//设置线的颜色为白色
setlinestyle(PS_SOLID, 3, 0);//设置线的格式, 实线,3个像素宽,0为自适应
if (postion == 'r')
{
circle(x, y, 15);//圆中圆
//画线,坐下简单的计算,圆心坐标定位, 左右减加5
line(x - 10, y, x + 10,y);//画横线
line(x + 3, y - 5, x + 10, y);//画箭头上横线, 从一个点画到 x+10,y这个点,调试 前面2个参数到合适位置
line(x + 3, y + 5, x + 10, y);//画箭头下横线,对称处理
}
else if (postion == 'l')//画了右边箭头,现在来画左边箭头,坐标处理一下
{
circle(x, y, 15);
line(x - 10, y, x + 10, y);
line(x - 3, y - 5, x - 10, y);
line(x - 3, y + 5, x - 10, y);
}
}
void drawMap()
{
//画背景
putimage(0, 0, &backImg);
//画小图 第0张和第1张
putimage(80, 130, carSmall + 0); //具体位置,用截图工具去测量,需要在哪里画 记住大致的位置 取整数即可
putimage(80, 330, carSmall + 1); //位置计算, 加上每一张小图的大小, 160+130=290,稍微大一点到310
//画大图
putimage(345,100,carBig+0);
drawButton(200, 90, 'r'); //画右边按钮
drawButton(115, 90, 'l'); //画左边按钮
//输出文字,显示标题
settextcolor(RED); //设置文字颜色为蓝色
settextstyle(25, 0, "STCAIYUN");//设置字体大小25到0,和字体 字体在c盘windows下的fonts文件夹 赋值字体名字
//setbkmode(TRANSPARENT); //有背景,去掉文字背景
outtextxy(350, 50, "微电子-机械之心");
}
文字是有背景的,取消setbkmode的注释,然后调整坐标350,50到 520,70,调整之后的显示如下图
按钮绘制完毕,我们接下来要做 人机交互了
3.3交互处理①(进入高亮,点击按钮事件,小图显示方框)
3.3.1 数学问题
现在我们思考一个问题,我直接画箭头不就好了,为什么要在填充圆里面画箭头?
因为判断鼠标是否在箭头上,是不是非常难处理?如果是在圆里面,可以用
上面是圆心在坐标原点,现在我们传递进来了以画按钮参数的圆心坐标,简化一下就是
sqrt函数我们要#include<math.h>头文件
3.3.2逻辑问题
①鼠标进入坐标范围类,按钮高亮变色,通过绘制其他颜色的按钮,封装为一个函数drawHighlightButton()
②鼠标离开,取消高亮,重新绘制白色按钮,
③需要单独调用drawButton()函数
3.3.3鼠标事件学习
void userMoveMouse()
{
//定义一个鼠标消息
MOUSEMSG m;
while (1) //死循环,一直监听
{
m = GetMouseMsg();
switch (m.uMsg)
{
case WM_MOUSEMOVE: //鼠标移动
//todo①判断鼠标是否在按钮上, 也就是鼠标左边在圆里面
break;
case WM_LBUTTONDOWN: //todo②鼠标左键按下,
break;
}
}
}
以上就是一个鼠标消息,通过死循环的方式,不断的判断鼠标是否移动,是否移动到圆内以及是否离开和是否点下
3.3.4判断鼠标是否在圆内,放入todu①内,由于用了sqrt()函数,记得在头文件引入math.h
/*****************************************
* 处理用户交互
* 主要就是处理鼠标操作
* userMoveMouse()
******************************************/
void userMoveMouse()
{
//定义一个鼠标消息
MOUSEMSG m;
while (1) //死循环,一直监听
{
// drawButton(200, 90, 'r'); //画右边按钮
// drawButton(115, 90, 'l'); //画左边按钮
m = GetMouseMsg();
switch (m.uMsg)
{
case WM_MOUSEMOVE: //鼠标移动
//判断鼠标是否在按钮上, 也就是鼠标是否在圆里面
if (sqrt((double)(m.x - 200)*(m.x - 200) + (m.y - 90)*(m.y - 90)) < 20) //之前确定的圆半径为20
{
//todo③
}
if (sqrt((double)(m.x - 115)*(m.x - 115) + (m.y - 90)*(m.y - 90)) < 20) //第二个圆
{
//todo④
}
break;
case WM_LBUTTONDOWN: //鼠标左键按下,todo⑤
break;
}
}
}
现在鼠标是否在圆内的判断条件已经写好了,我们运行一下发现是没有效果的,是因为高亮绘制按钮的代码,点击鼠标的事件函数等功能没有做好
3.3.5现在我们来封装drawHighlightButton()函数
/*****************************************
* 鼠标停留和离开按钮处理
* 处理用户交互
* drawHighlightButton()
******************************************/
void drawHighlightButton(int x, int y, char postion) //坐标,左边右边位置
{
//点击一下,把按钮变色, 就是重新绘制一边, 把上面的绘制按钮的代码复用
setfillcolor(BLACK);
solidcircle(x, y, 20);
setlinecolor(YELLOW);//设置线的颜色为白色
setlinestyle(PS_SOLID, 3, 0);
if (postion == 'r')
{
circle(x, y, 15);
line(x - 10, y, x + 10, y);
line(x + 3, y - 5, x + 10, y);
line(x + 3, y + 5, x + 10, y);
}
else if (postion == 'l')
{
circle(x, y, 15);
line(x - 10, y, x + 10, y);
line(x - 3, y - 5, x - 10, y);
line(x - 3, y + 5, x - 10, y);
}
}
3.3.6 调用调试(main函数调用userMoveMouse(),然后userMoveMouse()调用drawHighlightButton())
现在绘制高亮圆圈的函数已经封装好了,我们在userMouseMove()函数的todo③里面调用调试一下
/*****************************************
* 处理用户交互
* 主要就是处理鼠标操作
* userMoveMouse()
******************************************/
void userMoveMouse()
{
//定义一个鼠标消息
MOUSEMSG m;
while (1) //死循环,一直监听
{
// drawButton(200, 90, 'r'); //画右边按钮
// drawButton(115, 90, 'l'); //画左边按钮
m = GetMouseMsg();
switch (m.uMsg)
{
case WM_MOUSEMOVE: //鼠标移动
//判断鼠标是否在按钮上, 也就是鼠标在圆里面
if (sqrt((double)(m.x - 200)*(m.x - 200) + (m.y - 90)*(m.y - 90)) < 20) //之前确定的圆半径为20
{
drawHighlightButton(200,90,'r'); //重新绘制按钮在 之前的坐标,所以画按钮的坐标我们在上面就复制下来了
}
if (sqrt((double)(m.x - 115)*(m.x - 115) + (m.y - 90)*(m.y - 90)) < 20) //第二个圆
{
drawHighlightButton(115,90,'l');
}
break;
case WM_LBUTTONDOWN: //鼠标左键按下,
break;
}
}
}
当然,整个鼠标事件是通过userMoveMouse()函数接入的,所以需要在main()函数里面调用,不调用会没有效果.
int main()
{
initgraph(932, 538); // 根据素材像素大小创建同样大小窗口
loadResource(); //依然要记得调用刚刚的函数
drawMap();
userMoveMouse(); //调用鼠标事件,鼠标事件调用绘制高亮圆圈
getchar();
closegraph();
return 0;
}
现在高亮完成,但是鼠标离开后还是高亮,同时我们优化一下 鼠标是否在按钮内的if else逻辑链
3.3.7 离开按钮重新绘制白色按钮
void userMoveMouse()
{
//定义一个鼠标消息
MOUSEMSG m;
while (1) //死循环,一直监听
{
// drawButton(200, 90, 'r'); //画右边按钮
// drawButton(115, 90, 'l'); //画右边按钮
m = GetMouseMsg();
switch (m.uMsg)
{
case WM_MOUSEMOVE: //鼠标移动
//判断鼠标是否在按钮上, 也就是鼠标在圆里面
if (sqrt((double)(m.x - 200)*(m.x - 200) + (m.y - 90)*(m.y - 90)) < 20) //之前确定的圆半径为20
{
drawHighlightButton(200,90,'r');
}
else if (sqrt((double)(m.x - 115)*(m.x - 115) + (m.y - 90)*(m.y - 90)) < 20) //第二个圆
{
drawHighlightButton(115,90,'l');
}
else //处理当鼠标不在按钮上,还圆以前的按钮,就是把drawButton()单独调用一次,注意不要调用整个drawmap了,重新绘制为白色按钮就行
{
drawButton(200, 90, 'r');
drawButton(115, 90, 'l');
}
break;
case WM_LBUTTONDOWN: //鼠标左键按下,
break;
}
}
}
鼠标进入和离开的高亮,和取消高亮功能已经完成,下面我们来第四个要做的,点击按钮实现画小图的方框,以及方框的切换
3.4 点击按钮事件
逻辑是,当鼠标左键按下的时候,还是要判断是否是左边按钮和右边按钮,所以userMoveMouse()函数里面的 case WM_LBUTTONDOWN复用是否在圆内代码
case WM_LBUTTONDOWN: //鼠标左键按下, 画一个矩形
if (sqrt((double)(m.x - 200)*(m.x - 200) + (m.y - 90)*(m.y - 90)) < 20)
{
//封装一个画矩形的函数,为什么要画矩形, 因为要实现 点击箭头,矩形向下绘制的切换
}
//左边自己完善
else if (sqrt((double)(m.x - 115)*(m.x - 115) + (m.y - 90)*(m.y - 90)) < 20)
{
}
break;
然后画方框我们同样需要封装一个函数,切换的逻辑是:当我们点击按钮画一个蓝色方框,再点击的时候,下面小图画蓝色方框,同时把上面小图的方框绘制为白色(偷工处理,背景图片是白色),然后再点的时候,上图绘制蓝色方框,下图绘制白色方框,如此循环,因此复用的代码确实需要封装函数
3.4.1 drawRect()函数
功能分析,需要传递一个flag进来,标记为1,2,这样就可以反复循环切换上下方框的功能
/*****************************************
* 画小图片矩形
* drawRect()
******************************************/
void drawRect(int flag)//按等一下画上面的矩形,按第二下画下面那个矩形
{
if (flag == 1)//标记为1,画上小图的方框
{
setlinecolor(GREEN);
//rectangle();//左上角坐标到由下角坐标
//putimage(80, 130, carSmall + 0); //复制下上小图的绘制坐标方便方框坐标
//putimage(80, 330, carSmall + 1);
//左上角是80,130,小一点点
//rectangle(80-5,130-5,右下角的x,右下角的y);
// loadimage(carSmall + i, fileName, 150, 160); 照片缩放的大小代码
rectangle(80-5,130-5,80+150+5,130+160+5); //方框坐标就比图片大一点点
}
else if (flag == 2)//标记为2,画下小图的方框
{
setlinecolor(GREEN);
rectangle(80 - 5, 330 - 5, 80 + 150 + 5, 330 + 160 + 5); //下图就是纵坐标改为330开头就行
}
}
边写边调试,这个函数在哪里调用?
点击事件里面,所以在userMoveMouse()函数的case里面调用,加入调用代码,同时由于flag函数要传递,所以在userMoveMouse()函数里面要新增一个变量
int flag=1,userMoveMouse()代码如下
void userMoveMouse()
{
//定义一个鼠标消息
MOUSEMSG m;
int flag=1; //新增flag
while (1) //死循环,一直监听
{
// drawButton(200, 90, 'r'); //画右边按钮
// drawButton(115, 90, 'l'); //画右边按钮
m = GetMouseMsg();
switch (m.uMsg)
{
case WM_MOUSEMOVE: //鼠标移动
//判断鼠标是否在按钮上, 也就是鼠标在圆里面
if (sqrt((double)(m.x - 200)*(m.x - 200) + (m.y - 90)*(m.y - 90)) < 20) //之前确定的圆半径为20
{
drawHighlightButton(200,90,'r');
}
else if (sqrt((double)(m.x - 115)*(m.x - 115) + (m.y - 90)*(m.y - 90)) < 20) //第二个圆
{
drawHighlightButton(115,90,'l');
}
else //处理当鼠标不在按钮上,还圆以前的按钮,就是把drawButton()单独调用一次,注意不要调用整个drawmap了,重新绘制为白色按钮就行
{
drawButton(200, 90, 'r');
drawButton(115, 90, 'l');
}
break;
case WM_LBUTTONDOWN: //鼠标左键按下
if (sqrt((double)(m.x - 200)*(m.x - 200) + (m.y - 90)*(m.y - 90)) < 20)
{
//封装一个画矩形的函数,为什么要画矩形, 因为要实现 点击箭头,矩形向下绘制的切换
drawRect(flag);
//调用画矩形函数,传递flag进去
}
//左边自己完善
else if (sqrt((double)(m.x - 115)*(m.x - 115) + (m.y - 90)*(m.y - 90)) < 20)
{
drawRect(flag);
}
break;
}
}
}
现在点击按钮之后,第一个方框绘制成功,但是没有实现功能,我们需要完善一下drawRectange()的代码,让flag为1或者2的时候,做出不同的绘制行为
void drawRect(int flag)//按等一下画上面的矩形,按第二下画下面那个矩形
{
if (flag == 1)//标记为1,画上小图的方框
{
setlinecolor(GREEN);
//rectangle();//左上角坐标到由下角坐标
//putimage(80, 130, carSmall + 0); //复制下上小图的绘制坐标方便方框坐标
//putimage(80, 330, carSmall + 1);
//左上角是80,130,小一点点
//rectangle(80-5,130-5,右下角的x,右下角的y);
// loadimage(carSmall + i, fileName, 150, 160); 照片缩放的大小代码
rectangle(80-5,130-5,80+150+5,130+160+5); //方框坐标就比图片大一点点
//设置下一个边框的颜色为白色
setlinecolor(WHITE);
//把下面边框的坐标直接复制过来绘制一遍
rectangle(80 - 5, 330 - 5, 80 + 150 + 5, 330 + 160 + 5);
}
else if (flag == 2)//标记为2,画下小图的方框
{
setlinecolor(GREEN);
rectangle(80 - 5, 330 - 5, 80 + 150 + 5, 330 + 160 + 5); //下图就是纵坐标改为330开头就行
//设置上一个边框的颜色为白色
setlinecolor(WHITE);
//把上面边框的坐标直接复制过来绘制一遍
rectangle(80 - 5, 130 - 5, 80 + 150 + 5, 130 + 160 + 5);
}
}
这样编译时没有意义的,我们传入的是flag=1,只会画第一个方框,所以,flag变化的代码 我们要在userMoveMouse()的Case里面完善
每点击一下,让flag++,当flag==3的时候,flag复制为1
void userMoveMouse()
{
//定义一个鼠标消息
MOUSEMSG m;
int flag=1;
while (1) //死循环,一直监听
{
// drawButton(200, 90, 'r'); //画右边按钮
// drawButton(115, 90, 'l'); //画右边按钮
m = GetMouseMsg();
switch (m.uMsg)
{
case WM_MOUSEMOVE: //鼠标移动
//判断鼠标是否在按钮上, 也就是鼠标在右边圆里面
if (sqrt((double)(m.x - 200)*(m.x - 200) + (m.y - 90)*(m.y - 90)) < 20) //之前确定的圆半径为20
{
drawHighlightButton(200,90,'r');
}
else if (sqrt((double)(m.x - 115)*(m.x - 115) + (m.y - 90)*(m.y - 90)) < 20) //左边的处理
{
drawHighlightButton(115,90,'l');
}
else //处理当鼠标不在按钮上,还圆以前的按钮,就是把drawButton()单独调用一次,注意不要调用整个drawmap了,重新绘制为白色按钮就行
{
drawButton(200, 90, 'r');
drawButton(115, 90, 'l');
}
break;
case WM_LBUTTONDOWN: //鼠标左键按下
if (sqrt((double)(m.x - 200)*(m.x - 200) + (m.y - 90)*(m.y - 90)) < 20)
{
//封装一个画矩形的函数,为什么要画矩形, 因为要实现 点击箭头,矩形向下绘制的切换
drawRect(flag);
//
}
//左边自己完善
else if (sqrt((double)(m.x - 115)*(m.x - 115) + (m.y - 90)*(m.y - 90)) < 20)
{
drawRect(flag);
}
flag++;
if (flag == 3)
{
flag = 1; //每次按2下,就把标记归为1
}
break;
}
}
}
调试成功,接下来就是大图对应加框小图的切换了
3.4.2大图切换控制
大图与小图切换的逻辑,是需要对应图片的文件名,那么我们需要一个标记作为计数,计数器为i的时候,显示i.jpg就行
全局变量新增一个变量 smallNum
//开始尝试,点击切换大图片, 这里图片的切换,就需要计数器 ,在全局变量增加 新的变量
//全局变量
IMAGE backImg; //存放背景变量
IMAGE carBig[8];//存放大车
IMAGE carSmall[8];//存放小车
int smallNum = 0;//记录翻看照片的页码
切换时在点击的时候完成,所以在userMoveMouse()函数的鼠标点击事件,新增切换功能
//在鼠标点击事件里面修改代码
case WM_LBUTTONDOWN: //鼠标左键按下, 画一个矩形
if (sqrt((double)(m.x - 200)*(m.x - 200) + (m.y - 90)*(m.y - 90)) < 20)
{
//封装一个画矩形的函数,为什么要画矩形, 因为要实现 点击箭头,矩形向下绘制的切换
drawRect(flag);
//新增功能,大图切换
//putimage(345,100,carBig+0); //我们复制下大图的坐标位置 345,100
putimage(345, 100, carBig + smallNum); //依靠smallNum计数器,在345,100的位置
//时刻牢记, loadimage函数是加载资源决定图片大小,putimage函数决定放入的坐标和图片变量名
smallNum++;// 每点击一次,计数器加一,如果加到8怎么办?
}
userMoveMouse()函数代码如下
void userMoveMouse()
{
//定义一个鼠标消息
MOUSEMSG m;
int flag = 1;
while (1) //死循环,一直监听
{
// drawButton(200, 90, 'r'); //画右边按钮
// drawButton(115, 90, 'l'); //画右边按钮
m = GetMouseMsg();
switch (m.uMsg)
{
case WM_MOUSEMOVE: //鼠标移动
//判断鼠标是否在按钮上, 也就是鼠标左边在圆里面
if (sqrt((double)(m.x - 200)*(m.x - 200) + (m.y - 90)*(m.y - 90)) < 20) //之前确定的圆半径为20
{
drawHighlightButton(200, 90, 'r');
}
else if (sqrt((double)(m.x - 115)*(m.x - 115) + (m.y - 90)*(m.y - 90)) < 20) //第二个圆
{
drawHighlightButton(115, 90, 'l');
}
else //处理当鼠标不在按钮上,还圆以前的按钮,就是把drawButton()单独调用一次,注意不要调用整个drawmap了,重新绘制为白色按钮就行
{
drawButton(200, 90, 'r');
drawButton(115, 90, 'l');
}
break;
case WM_LBUTTONDOWN: //鼠标左键按下, 画一个矩形
if (sqrt((double)(m.x - 200)*(m.x - 200) + (m.y - 90)*(m.y - 90)) < 20)
{
//封装一个画矩形的函数,为什么要画矩形, 因为要实现 点击箭头,矩形向下绘制的切换
drawRect(flag);
//新增功能,大图切换
//putimage(345,100,carBig+0);
putimage(345, 100, carBig + smallNum);
smallNum++;
}
//左边自己完善
else if (sqrt((double)(m.x - 115)*(m.x - 115) + (m.y - 90)*(m.y - 90)) < 20)
{
drawRect(flag);
}
flag++;
if (flag == 3)
{
flag = 1; //每次按2下,就把标记归为1
}
break;
}
}
}
调试之后,发现新的问题,图片是有上限的,不能一直切换下去,当然小图切换的功能也没有加进去
这里我们就要相信分析一下逻辑:(smallNum初始值为0)
①点击次数为3的时候,也就是smallNum==2,点击了3次,左边的小图就得切换
②当我们点击了9次,也就是smallNum==8,要做一次计数器的轮回,继续切换
void userMoveMouse()
{
//定义一个鼠标消息
MOUSEMSG m;
int flag = 1;
while (1) //死循环,一直监听
{
// drawButton(200, 90, 'r'); //画右边按钮
// drawButton(115, 90, 'l'); //画右边按钮
m = GetMouseMsg();
switch (m.uMsg)
{
case WM_MOUSEMOVE: //鼠标移动
//判断鼠标是否在按钮上, 也就是鼠标左边在圆里面
if (sqrt((double)(m.x - 200)*(m.x - 200) + (m.y - 90)*(m.y - 90)) < 20) //之前确定的圆半径为20
{
drawHighlightButton(200, 90, 'r');
}
else if (sqrt((double)(m.x - 115)*(m.x - 115) + (m.y - 90)*(m.y - 90)) < 20) //第二个圆
{
drawHighlightButton(115, 90, 'l');
}
else //处理当鼠标不在按钮上,还圆以前的按钮,就是把drawButton()单独调用一次,注意不要调用整个drawmap了,重新绘制为白色按钮就行
{
drawButton(200, 90, 'r');
drawButton(115, 90, 'l');
}
break;
case WM_LBUTTONDOWN: //鼠标左键按下, 画一个矩形
if (sqrt((double)(m.x - 200)*(m.x - 200) + (m.y - 90)*(m.y - 90)) < 20)
{
//封装一个画矩形的函数,为什么要画矩形, 因为要实现 点击箭头,矩形向下绘制的切换
drawRect(flag);
if (smallNum == 8) //先处理最简单的情况,当图片计数器到末尾的时候,循环,并且计数器归零
{
//循环完之后要重新显示第0张和第1张
putimage(80, 130, carSmall + 0); //小图坐标是 80 130和 80 330
putimage(80, 330, carSmall + 1);
smallNum = 0;
}
if (smallNum == 2)
{
putimage(80, 130, carSmall + 2);
putimage(80, 330, carSmall + 3);
}
if (smallNum == 4)
{
putimage(80, 130, carSmall + 4);
putimage(80, 330, carSmall + 5);
}
if (smallNum == 6)
{
putimage(80, 130, carSmall + 6);
putimage(80, 330, carSmall + 7);
}
//新增功能,大图切换
//putimage(345,100,carBig+0);
putimage(345, 100, carBig + smallNum);
smallNum++; //计数器累加
}
//作业左边自己完善
else if (sqrt((double)(m.x - 115)*(m.x - 115) + (m.y - 90)*(m.y - 90)) < 20)
{
drawRect(flag);
//点击左边的按钮待完善
}
flag++;
if (flag == 3)
{
flag = 1; //每次按2下,就把标记归为1
}
break;
}
}
}
3.5背景音乐播放
音乐播放比较简单,只需要引用好相应的静态库就可以了
#define _CRT_SECURE_NO_WARNINGS
#include<stdio.h>
#include<graphics.h>
#include<math.h>
#include<Windows.h>
#include<mmsystem.h> //播放音乐头文件
#pragma comment(lib,"WINMM.LIB")//播放音乐静态库
为了防止出错,我们把windows.h也给引用下来
音乐播放是在资源加载完成,我们通过mciSendString()函数就可以,注意观察注释
//播放音乐,加载资源的时候播放
void loadResource()
{
mciSendString("open 1.mp3 alias music", 0, 0, NULL); //把MP3的文件名 赋值为别名music, 用默认的方式打开
mciSendString("play music repeat", 0, 0, NULL); // 用repat的方式重复播放音乐, 注意网易云等下载的音乐有封面,这个功能要把封面裁剪到500*500以下才能播放
loadimage(&backImg, "jiemian.jpg");
for (int i = 0; i <= 7; i++)
{
char fileName[20] = "";
// 0.jpg 1.jpg 2.jpg .....
sprintf(fileName, "%d.jpg", i); //把文件名 *.jpg 这个作为路径名写入数组, 那么循环8次,就初始化了8次
loadimage(carBig + i, fileName, 500, 400);
loadimage(carSmall + i, fileName, 150, 160);
}
}
大功告成一半,剩下的左边按钮的任务,依葫芦画瓢来进行把!
//完整代码,不要直接复制,最好自己调试
#define _CRT_SECURE_NO_WARNINGS
#include<stdio.h>
#include<graphics.h>
#include<math.h>
#include<Windows.h>
#include<mmsystem.h>
#pragma comment(lib,"WINMM.LIB")
/*****************************************
* 背景:需要一个变量
* 8张汽车照片:数组长度为8个变量
* 8张小缩放照片:数组长度为8个变量
******************************************/
//开始尝试,点击切换大图片, 这里图片的切换,就需要计数器 ,在全局变量增加 新的变量
//全局变量
IMAGE backImg; //存放背景变量
IMAGE carBig[8];//存放大车
IMAGE carSmall[8];//存放小车
int smallNum = 0;//记录翻看照片的页码
/*****************************************
* 画按钮
* drawButton();
* 画线函数 line(int x,int y,int xx,int yy) 两点确定一条线,三条线画一个箭头
* 画圆,实心圆solidcircle(x,y,R),空心圆 circle(x,y,R)
* 实心圆填充颜色 setfillcolor(),空心圆线条颜色 setlinecolor()
******************************************/
void drawButton(int x,int y, char postion) //按钮的坐标, 和朝向标记 <--- l ---> r
{
setfillcolor(BLACK);// 设置填充颜色为黑色
solidcircle(x, y, 20);//在x,y的位置画一个半径为20的圆
setlinecolor(WHITE);//设置线的颜色为白色
setlinestyle(PS_SOLID, 3, 0);//设置线的格式, 实线,3个像素宽,0为自适应
if (postion == 'r')
{
circle(x, y, 15);//圆中圆
//画线,坐下简单的计算,圆心坐标定位, 左右减加5
line(x - 10, y, x + 10,y);//画横线
line(x + 3, y - 5, x + 10, y);//画箭头上横线, 从一个点画到 x+10,y这个点,调试 前面2个参数到合适位置
line(x + 3, y + 5, x + 10, y);//画箭头下横线,对称处理
}
else if (postion == 'l')//画了右边箭头,现在来画左边箭头,坐标处理一下
{
circle(x, y, 15);
line(x - 10, y, x + 10, y);
line(x - 3, y - 5, x - 10, y);
line(x - 3, y + 5, x - 10, y);
}
}
/*****************************************
* 初始化资源
* 封装一个函数,初始化数据:初始化变量
* loadResource(); 没有返回值
******************************************/
void loadResource()
{
loadimage(&backImg, "jiemian.jpg");
//for循环批量加载:因为文件名有规律格式 从0.jpg到7.jpg
mciSendString("open 1.mp3 alias music", 0, 0, NULL); //把MP3的文件名 赋值为别名music, 用默认的方式打开
mciSendString("play music repeat", 0, 0, NULL); // 用repat的方式重复播放音乐, 注意网易云等下载的音乐有封面,这个功能要把封面裁剪到500*500
for (int i = 0; i <= 7; i++)
{
char fileName[20] = "";
// 0.jpg 1.jpg 2.jpg .....
sprintf(fileName, "%d.jpg", i); //把文件名 *.jpg 这个作为路径名写入数组, 那么循环8次,就初始化了8次
loadimage(carBig + i, fileName, 500, 400);
loadimage(carSmall + i, fileName, 150, 160);
}
}
/*****************************************
* 初始化界面
* 画界面, 画背景,画按钮, 画文字
* drawMap();
******************************************/
void drawMap()
{
//画背景
putimage(0, 0, &backImg);
//画小图 第0张和第1张
putimage(80, 130, carSmall + 0); //具体位置,用截图工具去测量,需要在哪里画 记住大致的位置 取整数即可
putimage(80, 330, carSmall + 1); //位置计算, 加上每一张小图的大小, 160+130=290,稍微大一点到310
//画大图
putimage(345,100,carBig+0);
drawButton(200, 90, 'r'); //画右边按钮
drawButton(115, 90, 'l'); //画左边按钮
//输出文字,显示标题
settextcolor(RED); //设置文字颜色为蓝色
settextstyle(25, 0, "STCAIYUN");//设置字体大小25到0,和字体 字体在c盘windows下的fonts文件夹 赋值字体名字
setbkmode(TRANSPARENT); //有背景,去掉文字背景
outtextxy(520, 70, "微电子-机械之心");
}
/*****************************************
* 鼠标停留和离开按钮处理
* 处理用户交互
* drawHighlightButton()
******************************************/
void drawHighlightButton(int x, int y, char postion) //坐标,左边右边位置
{
//点击一下,把按钮变色, 就是重新绘制一边, 把上面的绘制按钮的代码复用
setfillcolor(BLACK);
solidcircle(x, y, 20);
setlinecolor(YELLOW);//设置线的颜色为白色
setlinestyle(PS_SOLID, 3, 0);
if (postion == 'r')
{
circle(x, y, 15);
line(x - 10, y, x + 10, y);
line(x + 3, y - 5, x + 10, y);
line(x + 3, y + 5, x + 10, y);
}
else if (postion == 'l')
{
circle(x, y, 15);
line(x - 10, y, x + 10, y);
line(x - 3, y - 5, x - 10, y);
line(x - 3, y + 5, x - 10, y);
}
}
/*****************************************
* 画小图片矩形
* drawRect()
******************************************/
void drawRect(int flag)//按等一下画上面的矩形,按第二下画下面那个矩形
{
if (flag == 1)//标记为1,画上小图的方框
{
setlinecolor(GREEN);
//rectangle();//左上角坐标到由下角坐标
//putimage(80, 130, carSmall + 0); //复制下上小图的绘制坐标方便方框坐标
//putimage(80, 330, carSmall + 1);
//左上角是80,130,小一点点
//rectangle(80-5,130-5,右下角的x,右下角的y);
// loadimage(carSmall + i, fileName, 150, 160); 照片缩放的大小代码
rectangle(80-5,130-5,80+150+5,130+160+5); //方框坐标就比图片大一点点
//设置下一个边框的颜色为白色
setlinecolor(WHITE);
//把下面边框的坐标直接复制过来绘制一遍
rectangle(80 - 5, 330 - 5, 80 + 150 + 5, 330 + 160 + 5);
}
else if (flag == 2)//标记为2,画下小图的方框
{
setlinecolor(GREEN);
rectangle(80 - 5, 330 - 5, 80 + 150 + 5, 330 + 160 + 5); //下图就是纵坐标改为330开头就行
//设置上一个边框的颜色为白色
setlinecolor(WHITE);
//把上面边框的坐标直接复制过来绘制一遍
rectangle(80 - 5, 130 - 5, 80 + 150 + 5, 130 + 160 + 5);
}
}
/*****************************************
* 处理用户交互
* 主要就是处理鼠标操作
* userMoveMouse()
******************************************/
void userMoveMouse()
{
//定义一个鼠标消息
MOUSEMSG m;
int flag = 1;
while (1) //死循环,一直监听
{
// drawButton(200, 90, 'r'); //画右边按钮
// drawButton(115, 90, 'l'); //画右边按钮
m = GetMouseMsg();
switch (m.uMsg)
{
case WM_MOUSEMOVE: //鼠标移动
//判断鼠标是否在按钮上, 也就是鼠标左边在圆里面
if (sqrt((double)(m.x - 200)*(m.x - 200) + (m.y - 90)*(m.y - 90)) < 20) //之前确定的圆半径为20
{
drawHighlightButton(200, 90, 'r');
}
else if (sqrt((double)(m.x - 115)*(m.x - 115) + (m.y - 90)*(m.y - 90)) < 20) //第二个圆
{
drawHighlightButton(115, 90, 'l');
}
else //处理当鼠标不在按钮上,还圆以前的按钮,就是把drawButton()单独调用一次,注意不要调用整个drawmap了,重新绘制为白色按钮就行
{
drawButton(200, 90, 'r');
drawButton(115, 90, 'l');
}
break;
case WM_LBUTTONDOWN: //鼠标左键按下, 画一个矩形
if (sqrt((double)(m.x - 200)*(m.x - 200) + (m.y - 90)*(m.y - 90)) < 20)
{
//封装一个画矩形的函数,为什么要画矩形, 因为要实现 点击箭头,矩形向下绘制的切换
drawRect(flag);
if (smallNum == 8)
{
//循环完之后要重新显示第0张和第1张
putimage(80, 130, carSmall + 0); //小图坐标是 80 130和 80 330
putimage(80, 330, carSmall + 1);
smallNum = 0;
}
if (smallNum == 2)
{
putimage(80, 130, carSmall + 2);
putimage(80, 330, carSmall + 3);
}
if (smallNum == 4)
{
putimage(80, 130, carSmall + 4);
putimage(80, 330, carSmall + 5);
}
if (smallNum == 6)
{
putimage(80, 130, carSmall + 6);
putimage(80, 330, carSmall + 7);
}
//新增功能,大图切换
//putimage(345,100,carBig+0);
putimage(345, 100, carBig + smallNum);
smallNum++;
}
//作业左边自己完善
else if (sqrt((double)(m.x - 115)*(m.x - 115) + (m.y - 90)*(m.y - 90)) < 20)
{
drawRect(flag);
//点击左边的按钮待完善
}
flag++;
if (flag == 3)
{
flag = 1; //每次按2下,就把标记归为1
}
break;
}
}
}
//调试之后,发现新的问题,图片是有上限的,不能一直切换下去。
int main()
{
initgraph(932, 538); // 根据素材像素大小创建同样大小窗口
loadResource(); //依然要记得调用刚刚的函数
drawMap();
userMoveMouse(); //调用鼠标事件,鼠标事件调用绘制高亮圆圈
getchar();
closegraph();
return 0;
}