前言:
廢話不多說,先看最終效果圖

正文:
1、使用說明:
①引入頭檔案 “NumberScroller.h"
②用NumberScroller::create(int length,int fontSize) 函數來建立NumberScroller對象
③利用setNumber(int number)函數來設定計時器裡面的值(這裡隻實作了向後滾動,即設定的值要大于等于目前值)
④利用getNumber獲得目前計數器顯示的值
⑤利用setTime(float time)函數來設定計時器滾動的速度,預設為1
2、實作思路:
①根據輸入的長度length确定計時器的長度(即多少位)
②根據輸入的fontSize确定字型大小
③建立length條并排放置的豎着的從01234567890(注意末尾有個零)
④根據設定的數字,不同的豎條移動相應的位置。
⑤利用遮罩層遮罩
3、源碼解析:
對應思路①②
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
具體的初始化函數如下
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 初始化完的效果是這樣子(無遮罩)的:
NumberScroller最重要的函數還是setNumber
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方法
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函數
void NumberColumn::update(float d){
if(m_cur_num != m_target_num){<span style="white-space:pre"> </span>//如果目前顯示的數字不等于目标數字,即要開始滾動
float dis = update_speed*d;<span style="white-space:pre"> </span>//每次調用update函數需要滾動的距離等于update_speed 乘以 d (update_speed在setNumber函數中已經算出)
m_numbers->setPositionY(m_numbers->getPositionY() - dis);<span style="white-space:pre"> </span>//每次使整條向下移動dis距離
update_moveSum += dis;<span style="white-space:pre"> </span>//update_moveSum 用于儲存現在到底移動了多少距離
if(update_moveSum >= m_fontSize){<span style="white-space:pre"> </span>//如果現在已經移動了一個字大小的距離
m_cur_num++;<span style="white-space:pre"> </span>//目前數字+1
m_numbers->setPositionY( - m_cur_num%10 * m_fontSize);<span style="white-space:pre"> </span>//标準對齊
update_moveSum = 0; <span style="white-space:pre"> </span>//重置
}
}
}
update函數便是實作滾動效果的核心,每一行都給了詳細的注釋
因為我們這裡要實作的是類似于時鐘的滾動效果(秒針轉1圈時,分針轉1/60圈),應用到計時器,個位滾動10次,十位才會轉一下
是以對應每一條NumberColum,其對應的m_cur_num是不一樣的。
例如999這個數 ,第一條(百位)對應的m_cur_num應該是9,第二條(十位)對應的m_cur_num應該是99,第三條(個位)對應的m_cur_num應該是999
由于setNumber時候計算了速度:
int delta = m_target_num - m_cur_num;
update_speed = (delta * m_fontSize / m_time);
間隔越大,速度越快,是以保證了每條NumberColumn均會在m_time時間内完成滾動
有了上面的介紹,再回過頭來再看一次NumberScroller的setNumber函數,是不是恍然大悟呢
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;
}
}
}
完成效果後如下:
最後,添加遮罩層,隻顯示目前數字部分。
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);
}
關于遮罩的基本知識,推薦一個很好的blog
http://shahdza.blog.51cto.com/2410787/1561937
4、附上源碼:
點選打開連結