Cocos2d-x 3.2编写常用UI组件(二)滚动计数器NumberScroller

前言:

废话不多说,先看最终效果图

20150126144214109.gif


正文:

1、使用说明:

① 引入头文件 “NumberScroller.h"

② 用NumberScroller::create(int length,int fontSize) 函数来创建NumberScroller对象

③ 利用setNumber(int number)函数来设置计时器里面的值(这里只实现了向后滚动,即设置的值要大于等于当前值)

④ 利用getNumber获得当前计数器显示的值

⑤ 利用setTime(float time)函数来设置计时器滚动的速度,默认为1


2、实现思路:

① 根据输入的长度length确定计时器的长度(即多少位)

② 根据输入的fontSize确定字体大小

③ 创建length条并排放置的竖着的从01234567890(注意末尾有个零)

④ 根据设置的数字,不同的竖条移动相应的位置。

⑤ 利用遮罩层遮罩


3、源码解析:

对应思路①②

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
bool  NumberScroller::init( int  length, int  fontSize){  
     if (!Node::init())    return  false ;  
     m_length = length;  
     m_fontSize = fontSize;  
     m_visibleNode = Node::create();  
       
     //排好length列数字  
     for ( int  i=0;i<length;i++){  
         NumberColumn* column = NumberColumn::create(fontSize);  
         m_columns.pushBack(column);  
         column->setAnchorPoint(Point(0,0));  
         column->setPosition(i * fontSize,0);  
         column->setTime(m_time);  
         m_visibleNode->addChild(column);  
     }  
     /*设置遮罩层*/  
     ClippingNode* cliper = ClippingNode::create();  
     DrawNode* drawNode = DrawNode::create();  
     Point points[] = {Point(getPosition()),Point(getPositionX(),getPositionY()+m_fontSize),  
         Point(getPositionX()+m_length*m_fontSize,getPositionY()+m_fontSize),Point(getPositionX()+m_length*m_fontSize,getPositionY())};  
     drawNode->drawPolygon(points,4,Color4F(0,0,0,1),0,Color4F(0,0,0,1));  
       
     cliper->setStencil(drawNode);  
     cliper->addChild(m_visibleNode);  
     this ->addChild(cliper);  
}

先暂时不用看设置遮罩层部分,前面只是保存长度和字体大小到成员变量中,下面的一个for循环就是用来排好length列数字的。

其中这里出现了一个NumberColumn类,表示一列数字01234567890。

具体的初始化函数如下

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
bool  NumberColumn::init( int  fontSize){  
     if (!Node::init())    return  false ;  
     m_numbers = Node::create();  
     this ->addChild(m_numbers);  
     m_fontSize = fontSize;  
   
     this ->scheduleUpdate();  
   
     for ( int  i=0;i<10;i++){  
         char  str[2];  
         str[0] = i +  '0' ;  
         str[1] =  '\0' ;  
   
         Label* temp = Label::create(str, "" ,fontSize);  
         temp->setAnchorPoint(Point(0,0));  
         temp->setPosition(Point(0,i * fontSize));  
         m_numbers->addChild(temp);  
     }  
     char  str[2];  
     str[0]= '0' ;  
     str[1]= '\0' ;  
     Label* temp = Label::create(str, "" ,fontSize);  
     temp->setAnchorPoint(Point(0,0));  
     temp->setPosition(Point(0,10 * fontSize));  
     m_numbers->addChild(temp);  
   
     return  true ;  
}

NumberColumn的init函数里面,除了保存长度和字体大小和启动update外,接下来就是构造一个01234567890的竖列。

注意,我们这里把所有Label都放在一个Node里面,这样可使我们更方便的操作.

最后,回到NumberScroller  初始化完的效果是这样子(无遮罩)的:

20150126144708787.jpg

NumberScroller最重要的函数还是setNumber

1
2
3
4
5
6
7
8
9
10
void  NumberScroller::setNumber( int  number){  
     if (number > m_cur_num){  
         m_cur_num = number;  
   
         for ( int  i=0;i<m_length;i++){  
             m_columns.at(m_length - i -1)->setNumber(number);  
             number /= 10;  
         }         
     }  
}

可以看出都是间接地调用NumberColumn的setNumber方法

1
2
3
4
5
void  NumberColumn::setNumber( int  number, bool  direction){  
     m_target_num = number;  
     int  delta = m_target_num - m_cur_num;  
     update_speed = (delta * m_fontSize / m_time);  
}

奇怪啦,NumberColumn也只是简单的把传来的number保存到m_target_num里面,还有计算滚动的速度 也没有实现滚动的动作啊。


奥秘在于NumberColumn启动的update函数

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
void  NumberColumn::update( float  d){  
     if (m_cur_num != m_target_num){
     
         //如果当前显示的数字不等于目标数字,即要开始滚动  
         float  dis = update_speed*d;  //每次调用update函数需要滚动的距离等于update_speed 乘以 d (update_speed在setNumber函数中已经算出)  
         m_numbers->setPositionY(m_numbers->getPositionY() - dis);  //每次使整条向下移动dis距离  
         update_moveSum += dis;  //update_moveSum 用于保存现在到底移动了多少距离  
         if (update_moveSum >= m_fontSize){ 
             //如果现在已经移动了一个字大小的距离  
             m_cur_num++;  //当前数字+1  
             m_numbers->setPositionY( - m_cur_num * m_fontSize);  //标准对齐  
             update_moveSum = 0;  //重置  
         }  
     }  
}

update函数便是实现滚动效果的核心,每一行都给了详细的注释。


因为我们这里要实现的是类似于时钟的滚动效果(秒针转1圈时,分针转1/60圈),应用到计时器,个位滚动10次,十位才会转一下,所以对应每一条NumberColum,其对应的m_cur_num是不一样的。

例如999这个数 ,第一条(百位)对应的m_cur_num应该是9,第二条(十位)对应的m_cur_num应该是99,第三条(个位)对应的m_cur_num应该是999。

由于setNumber时候计算了速度:

1
2
int  delta = m_target_num - m_cur_num;  
update_speed = (delta * m_fontSize / m_time);

间隔越大,速度越快,因此保证了每条NumberColumn均会在m_time时间内完成滚动。


有了上面的介绍,再回过头来再看一次NumberScroller的setNumber函数,是不是恍然大悟呢:

1
2
3
4
5
6
7
8
9
10
void  NumberScroller::setNumber( int  number){  
     if (number > m_cur_num){  
         m_cur_num = number;  
   
         for ( int  i=0;i<m_length;i++){  
             m_columns.at(m_length - i -1)->setNumber(number);  
             number /= 10;  
         }         
     }  
}

 

完成效果后如下:

20150126145142177.gif

最后,添加遮罩层,只显示当前数字部分。

1
2
3
4
5
6
7
8
9
10
11
12
13
bool  NumberScroller::init( int  length, int  fontSize){  
         。。。省略了一部分。。。  
     /*设置遮罩层*/  
     ClippingNode* cliper = ClippingNode::create();  
     DrawNode* drawNode = DrawNode::create();  
     Point points[] = {Point(getPosition()),Point(getPositionX(),getPositionY()+m_fontSize),  
         Point(getPositionX()+m_length*m_fontSize,getPositionY()+m_fontSize),Point(getPositionX()+m_length*m_fontSize,getPositionY())};  
     drawNode->drawPolygon(points,4,Color4F(0,0,0,1),0,Color4F(0,0,0,1));  
       
     cliper->setStencil(drawNode);  
     cliper->addChild(m_visibleNode);  
     this ->addChild(cliper);  
}

关于遮罩的基本知识,推荐《【Cocos2d-x 3.2】裁剪节点(ClippingNode)总结


4、附上源码:滚动计数器NumberScroller

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值