好久没写博客了,这几天弄了一个椭圆旋转选择的界面,手生了好多东西都不记得了,这次用的引擎的cocos2.2.3
界面效果如下:
图片有个遮罩效果 后面的图片会变暗 前面的图片会变亮, 然后前面的图片会把后面的图片遮挡住。
图片旋转是根据滑动事件旋转的,基本原理与UIPageView差不多,但是调试椭圆轨迹、落点以及偏移角度弄了很多天,下面直接上代码:(头文件)
#ifndef SELECT_PEOPLE_TOUCH_LAYER
#define SELECT_PEOPLE_TOUCH_LAYER
#include
#include "cocos-ext.h"
#include "WelcomeScene.h"
#define MAX_NUM 16 //最多支持16个精灵在一个椭圆上
USING_NS_CC;
USING_NS_CC_EXT;
using namespace cocos2d::ui;
class WelcomeScene;
class SelectPeopleTouchLayer : public CCLayer
{
public:
static SelectPeopleTouchLayer* create(WelcomeScene* welcome_scene);
bool init();
void InitConfig(); //设置配置参数
void InitEllipse(); //初始化椭圆
void SetScene(WelcomeScene* welcome_scene);
virtual bool ccTouchBegan(CCTouch *pTouch, CCEvent *pEvent);
virtual void ccTouchMoved(CCTouch *pTouch, CCEvent *pEvent);
virtual void ccTouchEnded(CCTouch *pTouch, CCEvent *pEvent);
virtual void onEnter();
virtual void onExit();
//移动
virtual void handleMoveLogic(const CCPoint &touchPoint);
virtual void handleReleaseLogic(const CCPoint &touchPoint);
virtual bool scrollPages(float touchOffset);
//页面切换
void scrollToPage(int idx);
void movePages(float panyi_angle,int m_changeNum = -1);
void drawEffects(int who); // 绘制特效 parm:绘制对象的索引
void update(float dt);
float getWeight(float realAngle); //获取权重 parm:真实角度
protected:
inline float tuoyuanXat(float angle )//返回X坐标
{
//参数方程
return tuoyuan_a*cos(angle);
}
inline float tuoyuanYat(float angle )//返回Y坐标
{
return tuoyuan_b*sin(angle);
}
inline CCPoint gettuoyuanPointAt(float angle)//使用参数方程获取椭圆点
{
return ccp(tuoyuanXat(angle) + tuoyuan_center.x,tuoyuanYat(angle) + tuoyuan_center.y);
}
inline float getRealAngle(int pageIdx) //获取当前页面的偏移角度
{
float realAngle = ((int)(_nowAngle[pageIdx]*180/M_PI))%(360); //求余 且减90度
if(realAngle < 0) realAngle = 360 - abs(realAngle); //负角度转正角度
return realAngle; //真实偏移角度
}
inline float getAngleFromOffset(float offset)
{
if(offset != 0) return atan(offset/tuoyuan_b); //偏移角
else return 0.0f;
}
inline float radianToAngle(float radian) //弧度转换到角度
{
return radian * 180 / M_PI;
}
inline float angleToRadian(float angle) //角度转换到弧度
{
return angle * M_PI / 180;
}
private:
//精灵变量
CCArray* m_Childrens; //存储子节点 也就是精灵
UIImageView* _page[MAX_NUM]; //精灵的实例存储
float _nowAngle[MAX_NUM]; //当前各个精灵的角度 (非绝对角度,可能会大于360度)
float now_weight[MAX_NUM]; //当前精灵权重 用于判断特效以及显示层级
static WelcomeScene * p_welcome_scene; //顶层场景
//下面是涉及到滚动end事件触发的回滚效果的变量
bool _isAutoScrolling; //自动滚动 滑动的回滚效果
float _autoScrollSpeed; //滚动速度(越大滚动的越慢)
//变化的角度以及偏移量
float m_changeAngle;
float m_changeOffset;
int m_changeNum; //偏移倍数
int last_pageIdx; //滑动前的当前页
float m_endAngle; //滑动结束后 当前页的角度
float _touchStartLocation; //触摸开始时X的位置
float _touchMoveStartLocation; //一次移动前的开始位置(用于计算偏移)
int _curPageIdx; //当前页面 使用 _page[_curPageIdx] 可获取当前选中精灵
//椭圆参数
float tuoyuan_a;
float tuoyuan_b;
CCPoint tuoyuan_center;
};
#endif
#include "SelectPeopleTouchLayer.h"
#include "EllipseAction.h"
#include "cocos-ext.h"
#include
USING_NS_CC;
USING_NS_CC_EXT;
using namespace cocos2d::ui;
WelcomeScene * SelectPeopleTouchLayer::p_welcome_scene=NULL;
SelectPeopleTouchLayer* SelectPeopleTouchLayer::create(WelcomeScene* welcome_scene)
{
SelectPeopleTouchLayer* layer = new SelectPeopleTouchLayer();
if(layer)
{
layer->SetScene(welcome_scene);
if(layer->init())
{
layer->autorelease();
return layer;
}
}
delete layer;
layer = NULL;
return NULL;
}
bool SelectPeopleTouchLayer::init()
{
bool bRet = false;
do
{
CC_BREAK_IF(! CCLayer::init());
setTouchEnabled(true);
scheduleUpdate();
InitConfig();
InitEllipse();
bRet = true;
} while (0);
return bRet;
}
//初始化参数
void SelectPeopleTouchLayer::InitConfig()
{
CCSize visibleSize = CCDirector::sharedDirector()->getVisibleSize();
CCPoint origin = CCDirector::sharedDirector()->getVisibleOrigin();
//初始化参数
_isAutoScrolling = false; //默认不执行update
_curPageIdx = 0;
//设置椭圆参数
tuoyuan_a = 300;
tuoyuan_b = 100;
tuoyuan_center = ccp(origin.x + visibleSize.width/2,
origin.y + visibleSize.height/2);
}
//初始化椭圆
void SelectPeopleTouchLayer::InitEllipse()
{
const int length = m_Childrens->count();
for (int i=0; iobjectAtIndex(i);
ui->setPosition(gettuoyuanPointAt(angle));
_nowAngle[i] = angle;
_page[i] = ui;
float Aangle = radianToAngle(angle);
//设置当前页面
if(Aangle >= 269 && Aangle <= 271)
{
_curPageIdx = i; //页面
last_pageIdx = _curPageIdx;
}
//绘制特效
drawEffects(i);
}
}
//设置场景
void SelectPeopleTouchLayer::SetScene(WelcomeScene* welcome_scene) {
p_welcome_scene = welcome_scene;
//加载层
UILayer * ui=static_cast(p_welcome_scene->getChildByTag(1));
ui->addWidget(GUIReader::shareReader()->widgetFromJsonFile("NewUi_1.json"));
//存储椭圆的精灵
m_Childrens = ui->getWidgetByName("Panel_14")->getChildren();
}
bool SelectPeopleTouchLayer::ccTouchBegan(CCTouch *pTouch, CCEvent *pEvent){
_touchMoveStartLocation = convertToNodeSpace(pTouch->getLocation()).x;
_touchStartLocation = _touchMoveStartLocation;
m_endAngle = 0.0f; //滑动前初始化当前页偏移角度
CCLog("Touch began");
return true;
}
void SelectPeopleTouchLayer::ccTouchMoved(CCTouch *pTouch, CCEvent *pEvent)
{
handleMoveLogic(pTouch->getLocation());
}
void SelectPeopleTouchLayer::ccTouchEnded(CCTouch *pTouch, CCEvent *pEvent){
CCLog("Touch end");
handleReleaseLogic(pTouch->getLocation());
}
void SelectPeopleTouchLayer::handleReleaseLogic(const CCPoint &touchPoint)
{
int pageCount = m_Childrens->count();
float whith = 360.0f / (pageCount * 2); //偏移角度临界值 8个精灵的话是22.5°
float angle = radianToAngle(m_endAngle);
//判断是否需要切换页面
int p = angle / whith; //角度与22.5度做倍数比较
if(p < 0) //这个判断是获取到页面的偏移量
{
if(p % 2 != 0) p = (p - 1) / 2;
else p = p / 2;
}
else
{
if(p % 2 != 0) p = (p + 1) / 2;
else p = p / 2;
}
_curPageIdx = (_curPageIdx - p) % pageCount; //获取到新页面
if(_curPageIdx < 0) _curPageIdx += 7; //小于0 就回转
scrollToPage(_curPageIdx);
}
void SelectPeopleTouchLayer::scrollToPage(int idx)
{
_isAutoScrolling = true;
//需要变化的多大的角度
int length = m_Childrens->count();
_autoScrollSpeed = 10;
m_changeAngle = 270 - getRealAngle(_curPageIdx); //270 - 滑动页面当前角度(这是相对偏移的角度)
m_changeAngle = angleToRadian(m_changeAngle); //角度转弧度
m_changeOffset = tuoyuan_b * tan(m_changeAngle);
m_changeOffset = m_changeOffset / _autoScrollSpeed;
m_changeAngle = m_changeAngle / _autoScrollSpeed;
}
void SelectPeopleTouchLayer::handleMoveLogic(const CCPoint &touchPoint)
{
float offset = 0.0;
float moveX = convertToNodeSpace(touchPoint).x;
offset = moveX - _touchMoveStartLocation;
_touchMoveStartLocation = moveX;
scrollPages(offset);
}
bool SelectPeopleTouchLayer::scrollPages(float touchOffset)
{
movePages(getAngleFromOffset(touchOffset));
return true;
}
void SelectPeopleTouchLayer::movePages(float panyi_angle,int m_changeNum)
{
const int length = m_Childrens->count();
for (int i=0; isetPosition(gettuoyuanPointAt(_nowAngle[i]));
//特效
drawEffects(i);
}
}
void SelectPeopleTouchLayer::drawEffects(int who)
{
float realAngle = getRealAngle(who); //真实偏移角度
//计算权重
float Weight = getWeight(realAngle);
_page[who]->setColor(ccc3(255/Weight,255/Weight,255/Weight));
_page[who]->setZOrder(10-abs(Weight));
}
float SelectPeopleTouchLayer::getWeight(float realAngle)
{
//计算权重
float Weight = realAngle - 90;//减90°
Weight = 5 - abs(Weight / 45); //除以45 取绝对值 得到中间的权值
if(realAngle > 180) //这里是处理下部分的权值
{
Weight = abs(Weight-1);
Weight++;
}
if(Weight < 1) Weight = 1;
return Weight;
}
void SelectPeopleTouchLayer::onEnter()
{
CCDirector::sharedDirector()->getTouchDispatcher()->addTargetedDelegate(this,0,false);
CCLayer::onEnter();
CCLog("enter");
}
void SelectPeopleTouchLayer::onExit()
{
CCDirector::sharedDirector()->getTouchDispatcher()->removeDelegate(this);
CCLayer::onExit();
CCLog("exit");
}
void SelectPeopleTouchLayer::update(float dt)
{
if (_isAutoScrolling)
{
float realAngle = getRealAngle(_curPageIdx); //真实偏移角度
if(realAngle <= 271.0f && realAngle >= 269.0f)
{
last_pageIdx = _curPageIdx;
CCLog("OK name: %s now _curPageIdx = %d",_page[_curPageIdx]->getName() , _curPageIdx);
_isAutoScrolling = false;
}
movePages(m_changeAngle,--m_changeNum);
}
}
主要原理如下:
在刚开始触摸的时候,设置椭圆的初始角度,以及一些初始信息;
滑动的时候通过ccTouchMoved调用handleMoveLogic 这个函数主要是计算偏移量 并调用scrollPages函数,这个函数主要是将偏移量转换成偏移角度,传入movePages函数中,这个函数的后面一个参数是没有用的 忘记删除了,这个函数的作用是通过原来的角度加上偏移角度,获取最终精灵应该变换的角度,并且通过这个角度和椭圆的参数方程计算此时精灵应该占的位置,然后设置位置,最后调用特效函数drawEffects,这个函数中,我提出了一个权重的概念,权重是根据椭圆的角度进行计算的,最终结果是,越往后方,权重越高,最前方的权重是1。
触摸结束之后,调用handleReleaseLogic判断精灵是否翻页,以本文的8个精灵为例,只要当前页的偏移角度超过22.5或者-22.5,就进行翻页,但是,这有一个问题,就是超过45度,不会翻两页,于是我做出了一个算法,判定是22.5这个角度的多少倍(变量p),通过这这个倍数判断翻多少页。
判断页码之后,调用scrollToPage设定一些参数使精灵能自动归位,自动将离最下方点的精灵归位到最下方,设置完成后update中的语句会执行,这里面的语句的功能是将精灵按照设定的偏移量进行偏移,如果偏移量在271与269之间(存在误差),就停止执行这些语句。