首页 > 代码库 > DirectUI通用动画框架

DirectUI通用动画框架

        在编写VC界面时,编写动画比较困难,代码重用性不高。编写一个临时动画需要创建定时器或者线程来驱动改变渲染状态,来达到画面实时改变的目的。但是定时器和线程都是比较难以维护的,处理不好很容易造成资源浪费甚至程序崩溃。

         Skilla在上一周整理好了skillcore库,这一次又给它增添了通用动画框架。这个动画框架本身没有渲染功能,主要是提供动画的驱动事件,使用时需要自己去处理动画事件去完成动画渲染。该框架比较简单,动画由线程来驱动,下面展示一下具体的构成。


       根据动画的特点,就像播放动画片一样,Skilla把动画抽象成了一个动画基类BaseAnimation,里面包括动画的运行时间,运行状态,循环状态,正反序等等。使用时可以调用PlayAnimation,PauseAnimation,ResumeAnimation,StopAnimation等方法来控制动画的运行状态。动画本身还可以绑定动画监听器AnimationListener来触发动画状态改变时的事件,以方便处理。BaseAnimation本身是个抽象类,使用时需实现virtual bool FirstRun() = 0; virtual void Run() = 0; virtual bool LastRun() = 0;这三个接口,firstRun触发时,渲染动画的初始状态,lastRun触发时渲染动画的结束状态,Run触发时则根据runningTime这个时间轴属性来渲染动画每一帧的状态。为了方便使用Skilla实现了三个常用动画子类,PosChangeAnimation  AlphaChangeAnimation和SeqFrameAnimation,它们分别是位置改变动画,渐隐渐显(透明度改变)动画以及序列帧动画。关于使用方法在下面介绍。

      由于动画的生命周期难以管理,因为有的动画为临时动画,播放一次就再也不用了;而有的动画则需要重复使用,甚至绑定到控件上,和执行动画的控件同生共死。之所以出现这个问题就是因为把动画抽象成了类。这时候用指针来管理动画的生命周期明显是个坏主意,为了保证其通用性,我们采用动画工厂来管理动画的生命周期,创建动画时采用AnimationFactory动画工厂,操作现有动画时,使用AnimationFactory的FindAniamtion方法通过aniamtionName取到动画对象指针,删除动画同样要使用AnimationFactory通过animationName来删除。在应用程序退出时,需要AnimationFactory清理掉所有的剩余动画对象。

     下面以duilib界面库为例,看看具体如何使用的

class CloseAnimationListener : public AnimationListener
{
public:
	LRESULT OnStop(std::int64_t runningTime,std::int64_t totalTime,bool bReverse,bool bLoop)
	{
		AnimationFactory::GetInstance()->DeleteAnimation(L"PosChange.CloseAni");
		DuiApplicationBase::GetInstance()->RequestQuit();
		return AnimationListener::OnStop(runningTime,totalTime,bReverse,bLoop);
	}


};


class AlphaChangeListener : public AnimationListener
{
public:
	LRESULT OnPlayEnd(std::int64_t runningTime,std::int64_t totalTime,bool bReverse,bool bLoop)
	{
		BaseAnimation* p = AnimationFactory::GetInstance()->FindAnimation(L"AlphaChange.InitAni");
		p->SetReverse(!p->GetReverse());
		return 0;
	}
};

class CMainFrame
 : public WindowImplBase ,public IPosChangeAnimation,public ISeqFrameAnimation,public IAlphaChangeAnimation
{
public:
	CMainFrame(void);
	~CMainFrame(void);

	//duilib相关
	static CMainFrame* Instance();
	virtual CDuiString     GetSkinFolder();
	virtual CDuiString     GetSkinFile();
	virtual LPCTSTR       GetWindowClassName(void) const;
	CControlUI*            CreateControl(LPCTSTR pstrClass);


	virtual void SetAnimationPos(const RECT& rect)   //实现位置改变动画接口
	{
		CDuiRect r(rect);
		::MoveWindow(*this,r.left,r.top,r.right-r.left,r.bottom-r.top,false);
	}

	virtual void SetAnimationAlpha(const int Alpha)
	{
		CControlUI* pContrl = m_PaintManager.FindControl(L"alphaChange");
		String str;
		str.Format(L"file='alpha.png' fade='%d'",Alpha);
		pContrl->SetBkImage(str);
	}

	virtual void SetAnimationSeqFrame(const int framePos)   //实现序列帧动画接口
	{
		CControlUI* pControl = m_PaintManager.FindControl(L"bg");
		if (framePos==0)
		{
			pControl->SetBkImage(L"11.jpg");
		}else if (framePos==1)
		{
			pControl->SetBkImage(L"12.jpg");
		}else if (framePos==2)
		{
			pControl->SetBkImage(L"13.jpg");
		}else if (framePos==3)
		{
			pControl->SetBkImage(L"7.jpg");
		}
	}
	//窗口相关
	void InitWindow();
	LRESULT OnDestroy(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL& bHandled);
	LRESULT OnClose(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL& bHandled);
	LRESULT HandleMessage(UINT uMsg, WPARAM wParam, LPARAM lParam);
	void Notify(TNotifyUI& msg);


	ScopedPtr<Thread> initThread;
	ScopedPtr<Thread> destroyThread;
	ScopedPtr<CloseAnimationListener>  closeListener;
	ScopedPtr<AlphaChangeListener>   alphaListener;
};

首先,将被动画操作的控件事件对应的动画接口,这里以CMainFrame为动画控件,为了方便同时展现三种动画,直接给它把三个动画接口全部实现了,实现具体的动画渲染操作,另外还定义了两个动画监听器的子类CloseAnimationListener,AlphaChangeListener,用来监听动画的运行状态。


下面是实现部分

void CMainFrame::Notify(TNotifyUI& msg)
{
	if (_tcsicmp(msg.sType,_T("windowinit"))==0)
	{
		  closeListener = new CloseAnimationListener;
		  alphaListener = new AlphaChangeListener;
		  //创建一个序列帧动画
		  SeqFrameAnimation* animation = ( SeqFrameAnimation*)AnimationFactory::GetInstance()->CreateAnimation(L"SeqFrame.InitAni",SEQ_FRAME_ANIMATION);
		  animation->SetIntervalTime(600);   //设置时间间隔
		  animation->SetFrameSize(4);       //设置帧数
		  animation->BindObject(this);      //绑定对象
		  animation->SetLoop(true);         //设置循环
		  animation->PlayAnimation();       //开始播放

		  //创建一个渐隐渐显动画
		  AlphaChangeAnimation* animation2 = ( AlphaChangeAnimation*)AnimationFactory::GetInstance()->CreateAnimation(L"AlphaChange.InitAni",ALPHA_CHANGE_ANIMATION);
          animation2->RegistListener(alphaListener);
		  animation2->SetTotalTime(3000);
		  animation2->SetKeyFrameRect(0,255);
		  animation2->BindObject(this);      //绑定对象
		  animation2->SetLoop(true);         //设置循环
		  animation2->PlayAnimation();       //开始播放


	}else if (_tcsicmp(msg.sType,_T("click")) == 0)
	{
		if (_tcsicmp(msg.pSender->GetName(),_T("btn_close")) == 0)
		{
			PosChangeAnimation*  animation = (PosChangeAnimation*)AnimationFactory::GetInstance()->CreateAnimation(L"PosChange.CloseAni",POS_CHANGE_ANIMATION);
			animation->SetTotalTime(600);                       //设置时间
			RECT rect;
			GetWindowRect(*this,&rect);
			RECT rectTo = {rect.left,(rect.top+rect.bottom)/2,rect.right,(rect.top+rect.bottom)/2};
			animation->SetKeyFrameRect(rect,rectTo);              //设置位移
			animation->BindObject(this);                          //绑定动画对象
			animation->SetReverse(false);                         //设置正反向播放,如果不明白改变一下属性试试有什么效果
			animation->SetLoop(false);                            //设置是否循环播放
			animation->RegistListener(closeListener);             //绑定监听器
			animation->PlayAnimation();                           //开始播放
		}

	}
}

创建动画对象后,设置时间、位移、正反序等属性,绑定驱动对象以及添加监听器,完成这一系列初始化操作后就可以播放了,在播放过程中还可以任意修改动画属性。


下面是skillcore和Demo的下载链接:

skillcore动画Demo下载

 如有问题,或者建议请联系作者:Skilla(QQ:848861075)










DirectUI通用动画框架