duilib制作窗体动画效果

简介: 转载请说明原出处,谢谢~·http://blog.csdn.net/zhuhongshu/article/details/49026605        最近一段时间没写博客了,感觉最近没有遇到什么必须解决的bug。

转载请说明原出处,谢谢~·http://blog.csdn.net/zhuhongshu/article/details/49026605


        最近一段时间没写博客了,感觉最近没有遇到什么必须解决的bug。在一年前我把自己写的仿酷狗音乐播放器Demo写到博客时,我在博客末尾写过以后会做异形窗体和窗体动画的功能。异形窗体在半年前大概做完并且集成到我的库里了,但是窗体动画Demo没有写到博客。之前就有网友问我窗体动画的制作方法,一直懒着没写,不好意思···。

 

       今天把窗体动画的制作思路和Demo说明一下。实际上,异形窗体写完了,也就可以说窗体动画功能也写完了,因为窗体动画效果只是在使用半透明异形窗体技术基础上的一个应用。关于异形窗体的博客:使用duilib开发半透明异形窗体程序(附源码和demo)


        首先把效果图展示一下,本来是有79个特效,但是由于csdn博客的图片限制,只能录一小部分了···:



       这里特别声明,我使用的这个动画特效算法不是自己写的,而是使用了开源的界面库UiFeature的特效算法组件,非常感觉UiFeature作者的开源(UiFeature的下载地址:http://yunpan.cn/cZLr5zDIdW8ZT (提取码:ec84)),这同样是个很不错的界面库,UiFeature的动画特效是其一大亮点,而动画特效是个独立的DLL,我这里直接移植到我的Duilib窗体动画Demo里。


       说明一下窗体动画的开发步骤和注意点:

        1、开启窗体半透明异形功能(原生DuiLib不支持,不过包括我在内的不少维护Duilib的朋友的库都支持,其他的很多界面库也都支持),这个是根本,因为使用UpdateLayeredWindow函数使窗体拥有半透明异形功能后,就可以使用位图来绘制出窗体,而动画实际上就是有规律变化的连续位图。所以把动画位图定时贴到异形窗体上,就是窗体动画了~~。

       2、自定义一个容器控件,作为窗体的根容器

       3、开始动画时,截取自定义控件的渲染位图,用来计算动画效果

       4、为自定义容器增加一个定时器来用动画算法定时计算动画效果

       5、重写控件的DoPaint方法,把计算后的动画位图绘制出来


      实际上窗体动画是属于控件动画的范畴,在半透明异形窗体中,根容器的效果无疑就是窗体的效果,所以窗体动画的开发实际就转变为了控件动画的开发。把上述方法的根容器改为控件,那就是控件动画的开发步骤。


代码讲解:

     

       首先我的库支持透明异性功能,这满足了第一个要求。接下来定义一个容器控件,只要把他作为窗体xml的根容器,就可以做出窗体动画

class AnimLayout : public CVerticalLayoutUI, public IUIEffectCallBack
{
public:
	AnimLayout();
	~AnimLayout();

	virtual LPCTSTR GetClass() const override;
	virtual LPVOID GetInterface(LPCTSTR pstrName) override;
	virtual void DoPaint(HDC hDC, const RECT& rcPaint) override;
	virtual void SetAttribute(LPCTSTR pstrName, LPCTSTR pstrValue) override;

	bool StartEffect();
	void OnTimer(int iCurFrame);

public:
	// 每一 个 动画开始时回调
	virtual void OnUiEffectBegin(WPARAM effectKey, DWORD animaType) override;
	// 每一 个 动画结束时回调
	virtual void OnUiEffectEnd(WPARAM effectKey, DWORD animaType) override;
	// 每一 帧 动画绘制时回调
	virtual void OnUiEffectDraw() override;

private:
	bool				m_bPlaying;
	HDC					m_hMemDc;
	HBITMAP				m_hOldBitmap;
	HBITMAP				m_hTransBitmap;

	DWORD						m_dwEffectNum;
	IUiEffectManagerImpl*		m_pEffectManager;
	IUIEffect*					m_pEffect;

public:
	static const LPCTSTR kAnimLayoutClass;
	static const LPCTSTR kAnimLayoutInterface;
};

        当需要使用动画特效时,调用StartEffect函数,函数源码为:

bool AnimLayout::StartEffect()
{
	if (m_bPlaying)
		return false;
	if (m_dwEffectNum > 80)
	{
		m_dwEffectNum = 2;
		return false;
	}
		
	//LPDWORD pBmpBits = NULL;
	m_hMemDc = ::CreateCompatibleDC(m_pManager->GetPaintDC());
	m_hTransBitmap = CRenderEngine::GenerateBitmap(m_pManager, this, m_rcItem); 
	if (m_hTransBitmap == NULL)
		return false;
	m_hOldBitmap = (HBITMAP) ::SelectObject(m_hMemDc, m_hTransBitmap);

	BITMAP bmDst;
	GetObject(m_hTransBitmap, sizeof(bmDst), &bmDst);
	SIZE szMemDc = { bmDst.bmWidth, bmDst.bmHeight };

	//修补一下Alpha通道,一些控件(Richedit)会让Alpha为0
	RECT rcRestore = m_rcItem;
	CRenderEngine::RestoreAlphaColor((LPBYTE)bmDst.bmBits, bmDst.bmWidth, &rcRestore);

	// 填充动画参数
	AnimationParam animParam;
	animParam.effectKey = (WPARAM)this;				//控件指针
	animParam.animationEffect = m_dwEffectNum++;	//动画类型,从2-80,1为自定义动画,并没有移植过来
	animParam.animationFrequency = 20;				//动画间隔
	animParam.bShow = TRUE;							//动画顺序
	animParam.hBitmap = m_hTransBitmap;
	animParam.pBmpData = (BYTE*)bmDst.bmBits;
	animParam.bmpSize = szMemDc;
	animParam.hdc = m_hMemDc;

	BOOL bRet = m_pEffect->AppendAnimation(animParam);
	ASSERT(bRet);

	m_bPlaying = true;

	// 这里是同步执行的,Animation函数在动画完毕后返回
	bRet = m_pEffect->Animation(dynamic_cast<IUIEffectCallBack*>(this), 0);
	ASSERT(bRet);

	// 递归演示所有动画效果,这只是为了演示效果,实际开发不要这样做!
	StartEffect();

	return true;
}

        在代码里可以看到,开启动画时,调用CRenderEngine类的GenerateBitmap静态函数,使用这个函数可以获取到某个控件的渲染位图,这就满足了第三个要求。接下来应该是开启定时器来使用算法来定时计算位图,如果要精度要求不高那就可以直接调用CPaintManagerUI类的SetTimer方法,并且在自定义控件里响应定时消息来计算位图, 计算后调用Invalidate函数来刷新控件。


        不过UiFeature的这个特效组件的Animation函数,已经自带了定时功能,并且在填充了参数后会自动计算了位图,只要重写IUIEffectCallBack接口的OnUiEffectDraw方法来刷新窗体就可以了。所以和正常开发步骤有一些区别。


        最后重写DoPaint函数:

void AnimLayout::DoPaint(HDC hDC, const RECT& rcPaint)
{
	if (!m_bPlaying)
	{
		__super::DoPaint(hDC, rcPaint);
		return;
	}

	typedef BOOL(WINAPI *LPALPHABLEND)(HDC, int, int, int, int, HDC, int, int, int, int, BLENDFUNCTION);
	static LPALPHABLEND lpAlphaBlend = (LPALPHABLEND) ::GetProcAddress(::GetModuleHandle(_T("msimg32.dll")), "AlphaBlend");

	BLENDFUNCTION bf = { AC_SRC_OVER, 0, 255, AC_SRC_ALPHA };

	lpAlphaBlend(hDC, m_rcItem.left, m_rcItem.top, m_rcItem.right - m_rcItem.left, m_rcItem.bottom - m_rcItem.top, m_hMemDc,
		0, 0, m_rcItem.right - m_rcItem.left, m_rcItem.bottom - m_rcItem.top, bf);

}

        在函数里,判断如果正在使用动画特效,就不进行控件的正常绘制,而是直接调用AlphaBlend函数把计算后的位图贴到窗体dc上,就完成了第五步。 至此就做完了窗体动画!


总结:


        只要窗体动画的原理弄懂了,其实还是很简单的,几乎是比较固定的步骤,而动画制作的重点就转变为了动画算法。只要能找到或者设计出好的位图变换算法,就能做出很漂亮的窗体动画。

        我在做窗体动画Demo时做了两个,一个是文本提到的79变动画,另一个是仿QQ的窗体翻转动画(道理都是一样的,只是换了算法库,我就不另外说了)文中的Demo我会提交到我个人的Duilib库中:点击打开链接


Redrain QQ:491646717 2015.10.10


目录
相关文章
|
JSON Android开发 数据格式
原生app开发技巧——底部导航栏动画效果按钮制作方法之采用photoshop制作gif动画-过渡动画关键帧
原生app开发技巧——底部导航栏动画效果按钮制作方法之采用photoshop制作gif动画-过渡动画关键帧
原生app开发技巧——底部导航栏动画效果按钮制作方法之采用photoshop制作gif动画-过渡动画关键帧
|
人工智能 搜索推荐 C#
Photoshop和WPF双剑配合,打造炫酷个性的进度条控件
结合Photoshop和WPF,共同创建一个矢量的个性化进度条。
551 0
Photoshop和WPF双剑配合,打造炫酷个性的进度条控件
|
前端开发 C# Windows
WPF之鼠标滑动切换图片
原文:WPF之鼠标滑动切换图片   在网上找了一会儿也没找到我想要的效果,还是自己动手,丰衣足食吧。   需求:当前面板中只显示一张图片,图片栏的下部有用来显示当前图片处于图片队列中的位置的圆球,并且点击下部栏内的圆球可以快速切换,附动画缓动效果。
1231 0
|
C#
WPF中制作无边框窗体
原文:WPF中制作无边框窗体 众所周知,在WinForm中,如果要制作一个无边框窗体,可以将窗体的FormBorderStyle属性设置为None来完成。
1536 0
|
Web App开发 C# Windows
制作一个简单的WPF图片浏览器
原文:制作一个简单的WPF图片浏览器 注:本例选自MSDN样例,并略有改动。先看效果: 这里实现了以下几个功能:1.  对指定文件夹下所有JPG文件进行预览2.  对选定图片进行旋转3.  对选定图片进行灰度处理4.  对选定图片进行裁切处理5.  无限制的恢复功能6. 类似加入购物车的功能以下来看看其实现过程。
992 0
|
C#
WPF 关于圆角的制作
原文:WPF 关于圆角的制作 1、使用Boder(一般情况): 设置CornerRadius属性   ...   2、创建ClippingBorder类: View Code using System; using System.
1396 0
|
C#
WPF自定义控件第二 - 转盘按钮控件
原文:WPF自定义控件第二 - 转盘按钮控件 继之前那个控件,又做了一个原理差不多的控件。这个控件主要模仿百度贴吧WP版帖子浏览界面左下角那个弹出的按钮盘。希望对大家有帮助。 这个控件和之前的也差不多,为了不让大家白看,文章最后发干货。
1097 0