前言:毕业论文写完了,闲着没事干,研究研究这些一直想做的效果,先从桌面飘雪花开始,下面是过程记录。最后给出代码,供大家参考。

效果图:(桌面局部截图)

一、创建空WIN32工程并初始化

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

二、新建应用程序入口类(派生自CWinApp)

新建一个类(CSnowApp),该类派生自MFC的CWinApp类,(在头文件右击-》添加-》类)

头文件代码如下:

#pragma once#include "Common.h"// CSnowAppclass 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类封装过的,所以流程显得不那么明显。

三、雪花窗体显示

这里分析两个问题:

  • 我们并不是将每个雪花创建一个窗口,而是将当前屏幕做为窗体,在它上面画图而已。
  • 雪花的种类是有限的,即原始图片,这里加载了六个,而大家可以看到,整个屏幕的雪花量却是很大的,所以我们要建一个基类完成原始图像加载、窗体刷新等功能。而雪花类则建立在此基类的基础上,完成雪花的下落、移动等功能。

1、定义单个雪花图片对象

该对象包含每个雪花显示所需的所有信息,结构体定义如下:
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;

2、雪花窗体基类CLayeredWnd(派生自CWnd)

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;
}

根据给定的地址加载图片,并设计图片的高度和宽度信息。
增加图片:AddImage()

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;
}

根据给定的路径加载图片,并将其添加到数组中;
重绘窗口:ReDrawWindow

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更新到窗体上显示出来。

3、雪花显示窗体类CSnowWindow

头文件内容如下:

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();
}

讲解:
1、AddResImg()实现根据图片路径将图片加载到CLayeredWnd的m_ImageArray中

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();
}

讲解:
1、for循环中对每个雪花进行平移和下移。

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 (感谢本文作者,参考了他的源码)

利用GDI+基于WIN32实现桌面雪花效果(一)相关推荐

  1. android桌面雪花效果代码,Android营造雪花和雨滴浪漫效果

    本文在实现雪花效果的基础上,根据漫天飞舞雪花,实现下雨天场景的效果,使用eclipse android 版本,具体内容如下 雪花效果图: 具体代码: 1.漫天飞舞的雪花主要代码 SnowView pa ...

  2. android桌面雪花效果代码,桌面下雪花效果(DesktopSnowOK)

    圣诞节桌面下雪花模拟工具,让电脑屏幕下雪,飘起雪花的趣味软件. 让你的桌面呈现出不一样的风采,别具一格的个性化的色彩会让人眼前一亮! 特点: 1.可以自由调节桌面雪花的数量.透明度.下雪的速度.从而可 ...

  3. Delphi以GDI+制作桌面歌词效果

    分享一下我老师大神的人工智能教程!零基础,通俗易懂!http://blog.csdn.net/jiangjunshow 也欢迎大家转载本篇文章.分享知识,造福人民,实现我们中华民族伟大复兴! 在Win ...

  4. 在MFC中,利用GDI绘制橡皮筋效果-直线,圆,椭圆,矩形

    这段时间学习了GDI和GDI+:如果想实现橡皮筋效果,还是离不开GDI.虽然GDI+也能实现,但比较麻烦,有局限性,必须用到双缓冲. 下面贴出GDI绘制橡皮筋效果的示例代码 ZKCADView.h: ...

  5. 利用Python和win32编程范例——按需定制一个按键精灵

    转自:作者 橘子一方 http://www.orangecube.net/articles/python-win32-example.html 利用Python和win32编程范例--按需定制一个按键 ...

  6. 利用 Lotus Connections API 制作桌面应用

    作为 IBM Lotus™ Connections 1.0.1 的重大更新之一,开发人员可以利用该产品提供的轻量级 API 与已有的 Web 系统轻松实现数据整合.而本文将介绍如何利用这些 API,并 ...

  7. 推荐20款基于 jQuery CSS 的文本效果插件

    jQuery 和 CSS 可以说是设计和开发行业的一次革命.这一切如此简单,快捷的一站式服务.jQuery 允许你在你的网页中添加一些真正令人惊叹的东西而不用付出很大的努力,要感谢那些优秀的 jQue ...

  8. click 点击图片不起作用_JavaScript 练手小案例:基于SVG的图片切换效果

    最近太忙了,自动来到rjxy后,不晓得怎么回事,忙的都没时间更博了. 昨天还有个同学跟我说,你好久没更新博客了.. 甚为惭愧~~ 正好12月来了,今天开一篇. 最近上课讲到了 SVG,不晓得同学们理解 ...

  9. JavaScript 实现雪花效果

    JavaScript 实现雪花效果 一.实现功能 二.展示 1.代码展示 2.效果展示 总结 一.实现功能 (1)添加一个背景图片: (2)用js语言实现雪花飘落效果: (3)使用setInterva ...

最新文章

  1. 认识登录控件(Login、CreateUserWizard、LoginStatus和LoginName)
  2. Chapter10:观察者模式
  3. linux cmake编译源码,linux安装mysql(源码)以及cmake编译
  4. 5.MATLAB路径管理
  5. Java知识点梳理——继承
  6. sql 统计记录条数后 打印出所有记录_用SQL完成购买行为分析(下篇II)
  7. Java方法中的参数太多,第1部分:自定义类型
  8. 设计模式学习笔记-代理模式
  9. 【机器学习】知识框图总结
  10. bmp180气压传感器工作原理_各种传感器工作原理汇总
  11. aspose条件格式无法读取_分析 Pandas 源码,解决读取 Excel 报错问题
  12. Control Registers(CR0, CR1, CR2, CR3, and CR4)
  13. Spring ---- ssm整合
  14. 拓端tecdat|tableau的骑行路线地理数据可视化
  15. convertTO函数 简介
  16. vs201的vc++目录
  17. mpAndroidchart 坐标和图表距离_合二为一——在Excel中制作组合图表!
  18. OpenGL中各种坐标系的理解
  19. sub eax, _PAGESIZE; decrease by PAGESIZE test dword ptr [eax],eax ; probe page
  20. Opencv获取电脑摄像头抓拍的信息,

热门文章

  1. android 处理home键,android处理home键的方法
  2. office365服务器没有响应,修复:由于长时间运行的脚本,Office 365没有响应
  3. ssh端口转发(隧道技术)
  4. APP绕过模拟器-小白版
  5. 2015年薪酬大涨的15个IT岗位
  6. 1分钟解决 微信小程序 iPhone 11、iPhoneX 底部安全区域(底部小黑条)适配
  7. 网易云音乐用户微观洞察精细化运营
  8. python 库的安装(cmd+pip)
  9. 自己做的一个c#超大浮点数bigFloat
  10. Liunx操作-Record20—MMAP共享映射区相关的操作