导言:
由于项目新版本需要给每个弹出界面加上一个弹出效果,如果只是给一个或者几个界面加上这样的效果,其实就是简单的通过几个 runAction 去实现。但由于有很多个界面都要实现这样的效果,所以思考以后,我打算使用一个装饰器去实现这样的效果,装饰器会将一个普通的弹出框装饰成一个具有特殊弹出效果的弹出框。
之所以使用装饰器去实现这个功能,是因为这样可以将关于弹出效果的代码部分进行统一的管理,同时可以动态的将这些装饰功能赋予其他的界面,更灵活,更便捷,更统一。
实现:
DecorateFactory.h
- /**************
- **filename :DecorateFactory.h
- **author :hnliu
- **date : 2017.09.26
- **note:provide a factory to make some effect for a display node
- **the decorated node should be invoked addChild function
- **after the inner node invoked the setOnExitCallback
- */
- #pragma once
- #include “TypeDef.h”
- #include <functional>
- #include “cocos2d.h”
- class INodeDecorate;
- enum DecorateEnum
- {
- kNormalDecorate,
- kShadowDecorate,
- kAutoDecorate,
- kSwallowDecorate,
- kClickDismissDecorate,
- kHandleBackKeyDecorate,
- kTopDownDecorate,
- kEasePopupDecorate,
- kFadeDecorate,
- };
- class DecorateFactory
- {
- public:
- static cocos2d::Node* createDecorate(cocos2d::Node* node, const std::vector<int>& decorates, std::map<int,
- std::function<void()>> callbacks = {});
- private:
- static cocos2d::Node* __createByDecorateEnum(cocos2d::Node* node, DecorateEnum type, const std::function<void()>& cb);
- };
|
DecorateFactory.cpp
- #include “DecorateFactory.h”
- #include “INodeDecorate.h”
- //#include “NormalDecorate.h”
- //#include “AutoDismissDecorate.h”
- //#include “ClickDismissDecorate.h”
-
- //#include “ShadowDecorate.h”
- //#include “HandleBackKeyDecorate.h”
- #include “TopDownDecorate.h”
- #include “EasePopupDecorate.h”
- #include “SwallowTouchDecorate.h”
- #include “FadeDecorate.h”
- USING_NS_CC;
- Node* DecorateFactory::createDecorate(Node* node, const std::vector<int>& decorates, std::map<int, std::function<void()>>
- callbacks)
- {
- Node *nodeDecorate = node;
- for (auto it = decorates.begin(); it != decorates.end(); ++it)
- {
- std::function<void()> cb = nullptr;
- if (callbacks.find(*it) != callbacks.end())
- cb = callbacks.at(*it);
- nodeDecorate = __createByDecorateEnum(nodeDecorate, (DecorateEnum)*it, cb);
- }
- return nodeDecorate;
- }
- Node* DecorateFactory::__createByDecorateEnum(Node* node, DecorateEnum type, const std::function<void()>& cb)
- {
- Node *nodeDecorate = nullptr;
- switch (type)
- {
- case kEasePopupDecorate:
- nodeDecorate = EasePopupDecorate::create(node, cb);
- break;
- case kTopDownDecorate:
- nodeDecorate = TopDownDecorate::create(node, cb);
- break;
- case kSwallowDecorate:
- nodeDecorate = SwallowTouchDecorate::create(node, cb);
- break;
- case kFadeDecorate:
- nodeDecorate = FadeDecorate::create(node, cb);
- break;
- default:
- break;
- }
- return nodeDecorate;
- }
|
这个类有一个唯一的公有方法
- static cocos2d::Node* createDecorate(cocos2d::Node* node, const std::vector<int>& decorates, std::map<int,
- std::function<void()>> callbacks = {});
|
这个方法有三个参数,第一个参数是需要进行装饰的node指针,第二个参数是一个容器,表示要用哪些装饰器对这个node进行装饰,第三个参数是对应的装饰结束后进行的回调函数。
比如有一个界面,我们通过如下方法显示这个界面。这是一个帮助界面,当我们点击帮助按钮后,这个界面就会直接显示出来,没有任何的弹出效果。
- auto helpLayer = HelpLayer::create();
- Director::getInstance()->getRunningScene()->addChild(helpLayer);
|
这个时候,如果我想给这个帮助界面,加上弹出动画和底层触摸屏蔽的功能,就只需要使用这两种装饰器对这个界面进行装饰,使用方法如下:
- auto helpLayer = HelpLayer::create();
- Director::getInstance()->getRunningScene()->addChild(DecorateFactory::createDecorate(helpLayer, {kEasePopupDecorate, kSwallowDecorate},{}));
|
kEasePopupDecorate枚举值代表弹出效果装饰器,kSwallowDecorate枚举值代表底层触摸屏蔽装饰器。
弹出效果装饰器实现
EasePopupDecorate.h
- /*
- filename :EasePopupDecorate.h
- author :hnliu
- date : 2017.09.26
- note: ease popup layer
- */
- #pragma once
- #include “INodeDecorate.h”
- namespace cocos2d
- {
- class Node;
- class LayerColor;
- }
- class EasePopupDecorate : public INodeDecorate
- {
- public:
- CREATE_NODE_DECORATE_FUNC(EasePopupDecorate);
- protected:
- virtual bool init() override;
- private:
- EasePopupDecorate(Node *node, const std::function<void()>& callback)
- :INodeDecorate(node, callback){}
- private:
- cocos2d::LayerColor* m_colorLayer = nullptr;
- };
|
EasePopupDecorate.cpp
- #include “EasePopupDecorate.h”
- #include “cocos2d.h”
- USING_NS_CC;
- using namespace std;
- bool EasePopupDecorate::init()
- {
- Node::init();
- m_colorLayer = LayerColor::create({ 0, 0, 0, 0 });
- m_colorLayer->runAction(Sequence::create(DelayTime::create(0.15f), FadeTo::create(0.6f, 180), nullptr));
- m_colorLayer->addChild(m_topNode);
- auto children = m_topNode->getChildren();
- m_topNode->setScale(0.f);
- m_topNode->runAction(Sequence::create(ScaleTo::create(0.15f, 1.f)
- , ScaleTo::create(0.06f, 1.1f),
- ScaleTo::create(0.1f, 1.f),
- CallFunc::create([=]{
- if (m_onDismissCb) m_onDismissCb();
- }), nullptr));
- auto onExitCallback = m_topNode->getOnExitCallback();
- m_topNode->setOnExitCallback([=]{
- if (onExitCallback)
- onExitCallback();
- m_colorLayer->runAction(Sequence::create(FadeOut::create(0.15f), CallFunc::create([=]{ removeFromParent(); }), nullptr));
- auto l = Layer::create();
- l->setContentSize(Director::getInstance()->getWinSize());
- l->setAnchorPoint(Point::ANCHOR_MIDDLE);
- TRANSFER_CHILDREN(children, l);
- l->runAction(ScaleTo::create(0.125f, 0.f));
- addChild(l);
- });
- addChild(m_colorLayer);
- return true;
- }
|
这个类对参数node进行装饰,将参数这个node加到一个新的node中,同时给参数node加上缩放弹出效果,然后在新的node上加入一个淡入的黑色背景,最后返回这个全新的node。
底层触摸屏蔽装饰器实现
SwallowTouchDecorate.h
- /*
- filename :SwallowTouchDecorate.h
- author :hnliu
- date : 2017.09.30
- note: swallow touch
- */
- #pragma once
- #include “INodeDecorate.h”
- namespace cocos2d
- {
- class Node;
- class Touch;
- }
- class SwallowTouchDecorate : public INodeDecorate
- {
- public:
- CREATE_NODE_DECORATE_FUNC(SwallowTouchDecorate);
- protected:
- virtual bool init() override;
- bool _onTouchBegin(cocos2d::Touch* touch, cocos2d::Event* event, cocos2d::Node *node);
- void _onTouchEnd(cocos2d::Touch* touch, cocos2d::Event* event, cocos2d::Node *node){}
- void _onTouchCancel(cocos2d::Touch* touch, cocos2d::Event* event, cocos2d::Node *node){}
- private:
- SwallowTouchDecorate(Node *node, const std::function<void()>& callback)
- :INodeDecorate(node, callback){}
- };
|
SwallowTouchDecorate.cpp
- #include “SwallowTouchDecorate.h”
- #include “cocos2d.h”
- USING_NS_CC;
- using namespace std;
- bool SwallowTouchDecorate::init()
- {
- Node::init();
- addChild(m_topNode);
- auto onExitCallback = m_topNode->getOnExitCallback();
- m_topNode->setOnExitCallback([=]{
- if (onExitCallback) onExitCallback();
- runAction(Sequence::create(DelayTime::create(0.f), CallFunc::create([=]{ removeFromParent(); }), nullptr));
- });
- auto l = EventListenerTouchOneByOne::create();
- l->setSwallowTouches(true);
- l->onTouchBegan = std::bind(&SwallowTouchDecorate::_onTouchBegin, this, placeholders::_1, placeholders::_2, this);
- l->onTouchEnded = std::bind(&SwallowTouchDecorate::_onTouchEnd, this, placeholders::_1, placeholders::_2, this);
- l->onTouchCancelled = std::bind(&SwallowTouchDecorate::_onTouchCancel, this, placeholders::_1, placeholders::_2, this);
- Director::getInstance()->getEventDispatcher()->addEventListenerWithSceneGraphPriority(l, this);
- if (m_onDismissCb)
- m_onDismissCb();
- return true;
- }
- bool SwallowTouchDecorate::_onTouchBegin(cocos2d::Touch* touch, cocos2d::Event* event, cocos2d::Node *node)
- {
- return true;
- }
|
底层触摸屏蔽装饰器装饰后的node节点,就可以将触摸事件屏蔽掉,不会触摸穿透到底层的node。
通过装饰器工厂就可以很轻松的给一个没有任何效果的 layer 动态的加上一些属性,特性和效果。这就好像给一个裸体的人一件一件的穿上衣服,这也就是装饰器设计模式的特点。
遇到的问题:
在加上弹出框效果后,发现很多界面的弹出效果很不流畅。
原因:经过研究发现出现这种卡顿现象的主要原因是,在很多界面的 onEnter 方法中会去向 server 索取界面数据,由于消息系统的处理机制是,在得到 server 的消息后,会将 msg 加入到一个消息队列中,然后通过游戏引擎的主循环,每一帧从里面取一条 msg 进行处理。那么原因就很清晰了,由于界面的弹出效果需要每一帧都对界面的size进行计算和渲染,然后在这个过程中的某一帧,会去处理server送过来的 msg,如果这个处理的过程消耗了比较久的时间,弹出效果就会看起来很卡顿。
解决办法:解决办法就是在这个界面弹出动画结束以后,再向服务器去索取数据。
这个时候我们再去看这个唯一的公有方法
- static cocos2d::Node* createDecorate(cocos2d::Node* node, const std::vector<int>& decorates, std::map<int,
- std::function<void()>> callbacks = {});
|
发现,这个函数有一个callbacks的参数,这个参数的作用就是在某个装饰类型的动作结束时进行的回调函数。比如用 kEasePopupDecorate 装饰后,弹框会加入弹出动画,这个回调就是在弹出动画结束以后进行调用。
使用方法如下:
- auto helpLayer = HelpLayer::create();
- Director::getInstance()->getRunningScene()->addChild(DecorateFactory::createDecorate(helpLayer, {kEasePopupDecorate, kSwallowDecorate},{{kEasePopupDecorate, [=](){/*弹出动画结束后调用的方法,项目中在这个时候,向服务器获取数据*/}}}));
|
到此这篇文章就结束了,算是对我在项目中遇到的问题及解决办法的一个总结和纪录吧。