前言:毕业论文写完了,闲着没事干,研究研究这些一直想做的效果,先从桌面飘雪花开始,下面是过程记录。最后给出代码,供大家参考。
效果图:(桌面局部截图)
1、创建空WIN32工程(snow)
2、设置
项目-》属性-》配置属性-》MFC的使用-》在静态库中使用MFC
3、初始化GDI+和MFC库函数
新建一个Common.h文件,用来存放一些公用的结构体及代码,并在其中初始化GDI+,在其中放下如下代码来初始化GDI+
#include <gdiplus.h> #pragma comment(lib,"GdiPlus.lib") using namespace Gdiplus;
在stdafx.h中添加如下代码:(添加对MFC类和所有函数库支持)
#include <math.h> #define _ATL_CSTRING_EXPLICIT_CONSTRUCTORS // 某些 CString 构造函数将是显式的 // 关闭 MFC 对某些常见但经常可放心忽略的警告消息的隐藏 #define _AFX_ALL_WARNINGS #include <afxwin.h> // MFC 核心组件和标准组件 #include <afxext.h> // MFC 扩展 #include <afxdisp.h> // MFC 自动化类 #ifndef _AFX_NO_OLE_SUPPORT #include <afxdtctl.h> // MFC 对 Internet Explorer 4 公共控件的支持 #endif #ifndef _AFX_NO_AFXCMN_SUPPORT #include <afxcmn.h> // MFC 对 Windows 公共控件的支持 #endif // _AFX_NO_AFXCMN_SUPPORT
新建一个类(CSnowApp),该类派生自MFC的CWinApp类,(在头文件右击-》添加-》类)
头文件代码如下:
#pragma once #include "Common.h" // CSnowApp class CSnowApp : public CWinApp { DECLARE_DYNCREATE(CSnowApp) public: CSnowApp(); // 动态创建所使用的受保护的构造函数 virtual ~CSnowApp(); public: virtual BOOL InitInstance(); virtual int ExitInstance(); protected: DECLARE_MESSAGE_MAP() public: ULONG_PTR m_gdiplusToken; BOOL RegisterClass(LPCTSTR lpszClassName); }; extern CSnowApp theApp;这里重写了CWinApp的两个函数,InitInstance()和ExitInstance();
然后自己写了一个窗口类注册函数,完整代码如下:
BOOL CSnowApp::RegisterClass(LPCTSTR lpszClassName) { WNDCLASS wndcls; memset(&wndcls,0,sizeof(WNDCLASS)); wndcls.style=CS_DBLCLKS|CS_HREDRAW|CS_VREDRAW; wndcls.lpfnWndProc=::DefWindowProc; wndcls.hInstance=AfxGetInstanceHandle(); wndcls.hIcon=NULL; wndcls.hCursor=::LoadCursor(NULL,IDC_ARROW); wndcls.hbrBackground=(HBRUSH)(COLOR_BTNFACE+1); wndcls.lpszMenuName=NULL; wndcls.lpszClassName=lpszClassName; if(!AfxRegisterClass(&wndcls)) { TRACE("Class Registration Failed\n"); return FALSE; } return TRUE; }
BOOL CSnowApp::InitInstance() { // TODO: 在此执行任意逐线程初始化 //初始化GDI+ GdiplusStartupInput gdiplusStartupInput; GdiplusStartup(&m_gdiplusToken, &gdiplusStartupInput, NULL); //初始化应用程序 CWinApp::InitInstance(); //注册窗口类 if (!RegisterClass(L"CometAnimationUI")) { return FALSE; } //创建窗口 CSnowWindow snowWnd; snowWnd.Create(NULL); //将snowWnd作为主窗体HWND,传给m_pMainWnd,供其显示窗体 m_pMainWnd=&snowWnd; Run(); return TRUE; } int CSnowApp::ExitInstance() { // TODO: 在此执行任意逐线程清理 GdiplusShutdown(m_gdiplusToken); return CWinApp::ExitInstance(); }这里主要讲解InitInstance()的流程,这是应用程序初始化函数,它首先对GDI+进行初始化,然后注册窗口类,利用CSowWnd类创建窗口,然后将snowWnd作为主窗体HWND,传给m_pMainWnd,供其显示窗体。最后利用Run()函数进入消息循环。流程与WIN32窗体的创建过程一样,只是这里是经过CWinApp类封装过的,所以流程显得不那么明显。
这里分析两个问题:
typedef struct tagAnimationImage { Gdiplus::Image* pImage; int X; //图片的X坐标 int Y; //图片的Y坐标 int Width; //图片显示高度 int Height; //图片显示宽度 int Angle; //旋转角度 bool firstInit; //是否初步初始化,用于在初次初始化时设定该雪花是往左走还是往右走还是直线下落 int OffsetMode; //图片的行走方式,向左、向右、向下三种 } AnimationImage, *PAnimationImage,*LPAnimationImage;
CLayeredWnd头文件如下:
class CLayeredWnd : public CWnd { DECLARE_DYNAMIC(CLayeredWnd) public: CLayeredWnd(); virtual ~CLayeredWnd(); protected: DECLARE_MESSAGE_MAP() public: int m_nWidth; int m_nHeight; CArray<LPAnimationImage,LPAnimationImage> m_ImageArray;//图片数组 int m_ImageCount;//图片数量 public: // 载入图片 BOOL LoadImage(LPAnimationImage pImage,LPCTSTR lpName); // 添加图片到数组 LPAnimationImage AddImage(LPCTSTR lpName); // 释放图片数组 void ReleaseImage(); // 重新绘制窗口 void ReDrawWindow(void); // 虚函数 绘制窗口 virtual void OnDrawWindow(Gdiplus::Graphics* pGraphics); };成员变量讲解:
m_ImageArray:存储原始图片,即加载的六张雪花图;
m_ImageCount:数组的长度,即存储的原始图片的个数;
成员函数就不讲解功能了,每个函数上面都有标注;
具体实现:
加载图片:LoadImage()
BOOL CLayeredWnd::LoadImage(LPAnimationImage pImage,LPCTSTR lpPath) { //初始化空间 if(pImage->pImage) { delete pImage->pImage; pImage->pImage=NULL; } ZeroMemory(pImage,sizeof(AnimationImage)); //加载图片 pImage->pImage = Gdiplus::Image::FromFile(lpPath); if(!pImage->pImage) return FALSE; pImage->Width=pImage->pImage->GetWidth(); pImage->Height=pImage->pImage->GetHeight(); return TRUE; }根据给定的地址加载图片,并设计图片的高度和宽度信息。
LPAnimationImage CLayeredWnd::AddImage(LPCTSTR lpPath) { LPAnimationImage pImage=new AnimationImage; ZeroMemory(pImage,sizeof(AnimationImage)); LoadImage(pImage,lpPath); m_ImageArray.Add(pImage); m_ImageCount=m_ImageArray.GetCount(); return pImage; }根据给定的路径加载图片,并将其添加到数组中;
void CLayeredWnd::ReDrawWindow(void) { HDC hDC=::GetDC(m_hWnd); HDC hMemDC=::CreateCompatibleDC(hDC); BITMAPINFO bitmapinfo; bitmapinfo.bmiHeader.biSize = sizeof(BITMAPINFOHEADER); bitmapinfo.bmiHeader.biBitCount = 32; bitmapinfo.bmiHeader.biHeight = m_nHeight; bitmapinfo.bmiHeader.biWidth = m_nWidth; bitmapinfo.bmiHeader.biPlanes = 1; bitmapinfo.bmiHeader.biCompression=BI_RGB; bitmapinfo.bmiHeader.biXPelsPerMeter=0; bitmapinfo.bmiHeader.biYPelsPerMeter=0; bitmapinfo.bmiHeader.biClrUsed=0; bitmapinfo.bmiHeader.biClrImportant=0; bitmapinfo.bmiHeader.biSizeImage = bitmapinfo.bmiHeader.biWidth * bitmapinfo.bmiHeader.biHeight * bitmapinfo.bmiHeader.biBitCount / 8; HBITMAP hBitmap=CreateDIBSection (hMemDC,&bitmapinfo, 0,NULL, 0, 0); HBITMAP hOldBitmap = (HBITMAP)SelectObject (hMemDC,hBitmap); Graphics g(hMemDC); //------------------------------------------------------------- OnDrawWindow(&g); //设置透明窗口------------------------------------------------- CPoint DestPt(0,0); CSize psize(m_nWidth,m_nHeight); BLENDFUNCTION blendFunc32bpp; blendFunc32bpp.AlphaFormat = AC_SRC_ALPHA; blendFunc32bpp.BlendFlags = 0; blendFunc32bpp.BlendOp = AC_SRC_OVER; blendFunc32bpp.SourceConstantAlpha = 255; ::UpdateLayeredWindow(m_hWnd,hDC,NULL,&psize,hMemDC,&DestPt,0,&blendFunc32bpp,ULW_ALPHA); //释放资源------------------------------------------------- ::SelectObject (hMemDC,hOldBitmap); ::DeleteObject(hBitmap); ::DeleteDC(hMemDC); ::ReleaseDC(m_hWnd,hDC); }这里注意一点:首先是创建一个空白画布,然后调用虚函数OnDrawWindow()来画图,由于下面我们将在CLayeredWnd的基础上派生出CSnowWnd,所以在CSnowWindow我们会重新实现这个绘图函数,在这个函数中实现雪花的下移和平移功能。
最后在整个画布完成以后,利用UpdateLayeredWindow更新到窗体上显示出来。
头文件内容如下:
class CSnowWindow : public CLayeredWnd { public: CSnowWindow(void); ~CSnowWindow(void); private: CArray<LPAnimationImage,LPAnimationImage> m_SnowArray;//雪花图片数组 int m_SnowCount;//雪花图片数量 int m_RowCount;//每行图片数量 int m_AllCount;//可显示图片总数 public: DECLARE_MESSAGE_MAP() afx_msg void OnTimer(UINT_PTR nIDEvent); public: //创建窗体 BOOL Create(HWND hWndParent=NULL); //初始化初次显示界面 void InitImage(); //添加原始图片资源到CLayeredWnd的m_ImageArray中 void AddResImg(LPCTSTR lpName,int nAngle); //向m_SnowArray添加雪花图片 void AddSnow(int nCount); //下移雪花 void DownSnow(); //根据上限和下限,随机产生一个数字 int GetRndNum(int nMin,int nMax); public: //绘制当前界面,重写CLayeredWnd的此虚函数 virtual void OnDrawWindow(Gdiplus::Graphics* pGraphics); };由于每个变量和函数都对应有标注,这里就不再叙述了,下面看具体实现:
void CSnowWindow::InitImage() { AddResImg(_T("C:\\Snow01.png"),0); AddResImg(_T("C:\\Snow02.png"),0); AddResImg(_T("C:\\Snow03.png"),-1); AddResImg(_T("C:\\Snow04.png"),-1); AddResImg(_T("C:\\Snow05.png"),0); AddResImg(_T("C:\\Snow06.png"),0); AddResImg(_T("C:\\Snow07.png"),0); AddResImg(_T("C:\\Snow08.png"),0); AddResImg(_T("C:\\Snow09.png"),0); AddResImg(_T("C:\\Snow10.png"),0); AddResImg(_T("C:\\Snow11.png"),0); AddResImg(_T("C:\\Snow12.png"),0); m_RowCount=m_nWidth/(m_ImageArray[0]->Width+20); m_AllCount=m_RowCount*(m_nHeight/(m_ImageArray[0]->Height+20)); AddSnow(m_RowCount); } //添加要显示的雪花图片到CLayeredWnd的m_ImageArray中 void CSnowWindow::AddResImg(LPCTSTR lpName,int nAngle) { LPAnimationImage pImage=AddImage(lpName); pImage->Angle=nAngle; } //添加雪花图片 void CSnowWindow::AddSnow(int nCount) { for(int i=0;i<nCount;i++) { //随机取一张图片 int nIndex=GetRndNum(0,m_ImageCount-1); LPAnimationImage pImage=new AnimationImage; LPAnimationImage pSrcImage=m_ImageArray[nIndex]; CopyMemory(pImage,pSrcImage,sizeof(AnimationImage)); //随机设置图片的初始位置 pImage->X=GetRndNum(0,m_nWidth); pImage->Y=0-GetRndNum(pImage->Height,pImage->Height*2); //随机缩放图片 float f=(float)GetRndNum(50,100); f=f/(float)100; pImage->Width=(int)((float)pImage->Width*f); pImage->Height=(int)((float)pImage->Height*f); pImage->firstInit=true;//初始化为TRUE,此参数用来判定是否首次初始化OffsetMode参数 m_SnowArray.Add(pImage); } m_SnowCount=m_SnowArray.GetCount(); }讲解:
2、AddSnow()实现随机从m_ImageArray中取一张图片,并对其初始化,最后将其添加到m_SnowArray中。
3、InitImage()就是实现了初次显示的雪花图片的初始化工作,AddSnow(m_RowCount);将每行所具有的雪花个数的图片,以第一行显示出来。
在初次顶端雪花初始化完成以后,就要定时将雪花图片移动(下移和平移)
1、设置定时器
由于是定时刷新当前界面以使雪花下移,所以必定要设定定时器,并对WM_TIMER消息进行处理,下面看对WM_TIMER消息处理的代码:
void CSnowWindow::OnTimer(UINT_PTR nIDEvent) { // TODO: 在此添加消息处理程序代码和/或调用默认值 CLayeredWnd::OnTimer(nIDEvent); DownSnow(); }从这里可以看到,每次产生的定时脉冲所做的事情就是将雪花下移。下面看DownSnow()的代码:
//下移雪花图片 void CSnowWindow::DownSnow() { int nTop=25; for(int i=m_SnowCount-1;i>=0;i--) { LPAnimationImage pImage=m_SnowArray[i]; pImage->Y+=5;//下移 if(pImage->Y>m_nHeight)//超出屏幕高度 { m_SnowArray.RemoveAt(i);//移除这张图片 delete pImage; continue;//转到下一次循环 } //------------- if(pImage->Y<nTop) nTop=pImage->Y;//找到当前最后一个雪花离屏幕顶部的距离 //横向移动 if (pImage->firstInit) { pImage->firstInit=false; pImage->OffsetMode=GetRndNum(1,3); } switch (pImage->OffsetMode) { case 1: pImage->X--; break; case 2: pImage->X++; break; } } m_SnowCount=m_SnowArray.GetCount(); int nCount=m_AllCount-m_SnowCount; if(nCount>0 && nTop>20)//设定每行的竖向间隔,这里设定每20个像素显示一行雪花 { if(nCount>m_RowCount) nCount=m_RowCount; AddSnow(nCount); } ReDrawWindow(); }讲解:
2、如何产生下一行雪花。
注意这里有几句代码:
首先定义了一个距离,25个像素。
int nTop=25;
这句处在FOR循环内,由于pImage->Y每次都会递加5,所以到最后就会出现一种情况,其它雪花纵坐标都大于25,只有一个雪花的纵坐标小于25,所以这句的目的就是查找最后一个仍小于nTop的值。由于Y值每次递加5,所以最后一个Y值肯定落在20-25之间。
if(pImage->Y<nTop) nTop=pImage->Y;//找到当前最后一个雪花离屏幕顶部的距离
由于当雪花落在屏幕边界外后,会将雪花从数组中删除,而且随着雪花的下落,屏幕上还要继续落雪花,新产生的行所具有的要求是:
(1)、数量不能超,而且不能少
这里使用int nCount=m_AllCount-m_SnowCount;nCount=m_RowCount;来设定每行要显示的雪花数量。
(2)、每行雪花的行距
上面说了利用nTop来找到最后一个雪花离屏幕的距离(20-25),所以当nTop>20的时候,说明是时候显示下一行了,这时才可以显示,不然就挤在一块了。
m_SnowCount=m_SnowArray.GetCount(); int nCount=m_AllCount-m_SnowCount; if(nCount>0 && nTop>20)//设定每行的竖向间隔,这里设定每20个像素显示一行雪花 { if(nCount>m_RowCount) nCount=m_RowCount; AddSnow(nCount); }
3、调用CLayeredWnd的ReDrawWindow()
由于在ReDrawWindow中调用了虚函数OnDrawWindow(),根据虚函数性质,会调用CSnowWindow重写的OnDrawWindow函数,看OnDrawWindow的实现:
// 虚函数 绘制窗口 void CSnowWindow::OnDrawWindow(Gdiplus::Graphics* pGraphics) { for(int i=0;i<m_SnowCount;i++) { LPAnimationImage pImage=m_SnowArray[i]; pGraphics->DrawImage(pImage->pImage,pImage->X,pImage->Y,pImage->Width,pImage->Height); } }实现方法就是将每一个雪花图片重新根据新坐标绘制出来,仅而而已。
到这里,本文雪花飘落相关的东东已经介绍完成了,下篇在此基础上完成雪花在下落过程中实现旋转的功能。
源码来啦:http://download.csdn.net/detail/harvic880925/6999829
请大家尊重原创,转载请标明处出,谢谢!!!本文出处:http://blog.csdn.net/harvic880925/article/details/20565125
相关网页:
《深入解析MFC -- CWinApp》:http://blog.sina.com.cn/s/blog_660ca10d0100lg0z.html
《MFC中的CWnd类与Windows窗体的关系》:http://blog.tianya.cn/blogger/post_read.asp?BlogID=4361806&PostID=44293548
《桌面动画-雪花和兔子-源码分享》http://blog.csdn.net/cometnet/article/details/17332699 (感谢本文作者,参考了他的源码)