C++工具箱(三)——动画类之基类

来源:互联网 发布:淘宝店铺招牌图片素材 编辑:程序博客网 时间:2024/06/05 20:22

OK,我们已经讨论了单件和定时器了,该来点真格的了。这次我们来看看如何通过前面所述的定时器实现动画。我目前想到的动画有大小更改、Alpha 透明度更改、窗口位置更改等几种,后续会逐步加上。


我们先来思考动画的实质。动画其实就是通过定时器,一步步逼近一个期望结果的过程。我们期望的动画类是这样的:

1、接口简单、使用方便;

2、可扩展。比如我们后续加上新的动画类时不必太费力;

3、添加新动画类时不必操心动画共有的部分;(如计时器、回调等等)


一个动画有以下共有性质:

1、两次动画间的时间间隔(ms 为单位);

2、动画是否已经开启了;

3、整个动画展示完成后的回调(可选);

4、动画对象的管理


第四个尤为重要,因为我们可能在一个如何一个函数(全局函数、类成员方法、某一个线程处理函数)中启用一个动画,如果还需手动管理动画对象的生命周期的话,那就太不Fashion了!(你可以思考下如果让你来设计,你会怎么设计这个动画的基类)


好的,我们来看我设计的动画基类,如果你有更好的点子,可以找我沟通。你可能需要定时器、Boost相关的知识。如下:

/** * \file frameanimation.h * \authorarnozhang * \date2012.9.13 * \brief窗口动画基类. */#ifndef__FRAME_ANIMATION_BASE_H__#define__FRAME_ANIMATION_BASE_H__#include "wnd.h"#include "timer.h"namespace Util{template <typename _Co_Animation> class CFrameAnimation{public:    // 动画完成回调.    typedef _Co_Animation this_class;    typedef boost::function<void(void)> AnimationFinishCallback;    // 获取一个动画对象.    static _Co_Animation& GetAnimation(CWnd* targetFrame)    {        return *(new _Co_Animation(targetFrame));    }    // 启动动画.    void StartAnimation()    {        if (m_bAnimationStarted || !m_targetFrame)        {            return ;        }        m_bAnimationStarted = TRUE;        if (_PrepareAnimation())        {            Util::SetTimerCallback(                m_targetFrame,                m_dwTimeInterval,                TIMER_CALL_BIND(this_class, AnimationHandler_Bind_Proc)                );        }    }    // 设置动画时间间隔.    void SetTimeInterval(        DWORD dwTimeInteval = DEFAULT_ANIMATION_TIME_INTERVAL        )    {        m_dwTimeInterval = dwTimeInteval;    }// 设置动画帧数.void SetFrameCount(DWORD dwFrameCnt){m_dwFrameCount = dwFrameCnt;}    // 设置整个动画完成后的回调.    void SetFinishedCallback(const AnimationFinishCallback& finishedCbk)    {        delete m_pFinishedCallback;        m_pFinishedCallback = new AnimationFinishCallback(finishedCbk);    }private:    // 动画处理器.    void AnimationHandler_Bind_Proc()    {        _AnimationHandler();    }protected:    virtual bool _PrepareAnimation() = 0;    virtual void _AnimationHandler() = 0;    // 动画执行完毕.    void _AnimationFinished()    {        // 清除计时器.        Util::KillTimerCallback(m_targetFrame);        // 调用完成回调.        if (m_pFinishedCallback)        {            (*m_pFinishedCallback)();        }        // 删除动画对象.        delete this;    }    CFrameAnimation(CWnd* targetFrame)    {        m_targetFrame       = targetFrame;        m_dwTimeInterval    = DEFAULT_ANIMATION_TIME_INTERVAL;        m_dwFrameCount= 0;        m_bAnimationStarted = FALSE;        m_pFinishedCallback = 0;    }    virtual ~CFrameAnimation()    {        delete m_pFinishedCallback;    }    CWnd*m_targetFrame;    DWORDm_dwFrameCount;private:    AnimationFinishCallback*    m_pFinishedCallback;    DWORD                       m_dwTimeInterval;    BOOL                        m_bAnimationStarted;    static const int DEFAULT_ANIMATION_TIME_INTERVAL = 30;};#define BIND_ANIMATION_FINISHED_PROC(class_name, method_name) \    boost::bind(&class_name::method_name, this)} /*namespace Util ends here.*/#endif /*__FRAME_ANIMATION_BASE_H__*/

我们定义了一个抽象模版基类 CFrameAnimation。其中模版参数 _Co_Animation是实现动画类的具体类(如CFrameBlendAnimationCFrameSizeAnimation等)。如果我们要实现新的动画类,只需继承该类就行了,并且重写_PrepareAnimation_AnimationHandler这两个虚方法即可。其中_PrepareAnimation为开启动画前的初始化及准备工作——因为不同的动画类有不同的属性和初始化方法;_AnimationHandler正如其名,是动画处理过程,因为不同的动画类对应的动画处理也不尽相同。这两个方法都是模版方法


我们将 CFrameAnimation的构造和析构均设为protected属性(CFrameAnimation的子类亦如此),让它们只能通过静态方法GetAnimation得到一个动画对象,让动画对象只能在堆上构造,然后在动画完成后通过delete this; 这句来释放掉——完全保证了动画对象生命周期的独立管理,不需要用户操一点心思。


我们定义了一个 _AnimationFinished方法,每个新的动画类在_AnimationHandler中完成所有动画的处理后,必须调用该函数删除定时器、释放资源。考虑 DEFAULT_ANIMATION_TIME_INTERVAL 为什么为30?因为人眼中,当动画、游戏等帧数为每秒30帧左右的时候,看起来是连贯的。所以这个值为 1000ms / 30 帧,大约等于30。所以,动画对象的 SetTimerInterval 方法一般不需要调用,我们一般通过SetFrameCount 方法来控制动画的播放帧数。


比如我们实现了一个CFrameBlendAnimation来完成窗口的透明度渐变,那么使用方法也很简单:

#include "frameblendanimation"void animationFinished(){cout<<"hello! Animation Finished!"<<endl;}void foo(){CWnd wnd;wnd.Create();// ...CFrameBlendAnimation& animation = CFrameBlendAnimation::GetAnimation(&wnd);animation.SetStartAlpha(255);animation.SetEndAlpha(0);// 设置播放帧数.animation.SetFrameCount(10);// 完成回调可要可不要,根据需求来.animation.SetFinishedCallback(animationFinished);// 启动动画.animation.StartAnimation();}

可以看出,我们只需通过 GetAnimation 方法获取一个动画对象,然后做相应的设置,最后调用StartAnimation启动动画就行了,完全不用我们操心。解耦的好处在这里得到了充分体现。


下一章将实现一个实现上述窗口透明度渐变的动画类 CFrameBlendAnimation。将会有一份实现好的源代码示例可供下载微笑。大家也可趁机想想如何实现这样一个CFrameBlendAnimation类。