由于项目需求大厅里的牌子要循环滚动(tips:这样看起来东西比较多,其实是个挺搞笑的需求),cocos本身的控件不满足需求,所以就简单写了个。目前惯性减速不太好用,有更好解决方案的大拿给个提示啊!
GitHub链接
代码如下:
//
// ScrollCircle.hpp
// ScrollSelector-mobile
//
// Created by 徐家伟 on 2018/1/19.
//
#ifndef CycleScroll_hpp
#define CycleScroll_hpp
#include "cocos2d.h"
#include "cocos-ext.h"
#include <vector>
#define MIN_SCROLL_SPEED 1
#define MAX_SCROLL_SPEED 30
class CycleScroll: public cocos2d::LayerColor
{
CycleScroll();
public:
static CycleScroll* create(cocos2d::Size &size, std::vector<cocos2d::Node*> nodes, float distance, float minScale = 1.0f);
void ScrollTo(int index, float delay);
void setDisplaySize(const cocos2d::Size &size);
const cocos2d::Size& getDisplaySize() const;
private:
float minScale;
float spaceDistance;
int currentIndex;
cocos2d::Size disSize;
cocos2d::ClippingNode* clipNode;
std::vector<cocos2d::Node*> nodes;
// 减速使用
bool dragging;
bool autoScrolling;
float scrollDistance;
float startTime;
cocos2d::Vec2 startPos;
bool onTouchBegin(cocos2d::Touch *touch, cocos2d::Event *unused_event);
void onTouchMove(cocos2d::Touch *touch, cocos2d::Event *unused_event);
void onTouchEnd(cocos2d::Touch *touch, cocos2d::Event *unused_event);
void updateNodePosX(float interval);
virtual bool init();
void initView();
void initListener();
void update(float dt);
void deaccelerateScrolling(float dt);
CREATE_FUNC(CycleScroll);
};
#endif /* ScrollCircle_hpp */
#include "CyclyScroll.hpp"
using namespace std;
USING_NS_CC;
CycleScroll::CycleScroll():autoScrolling(false),
dragging(false),
scrollDistance(0),
minScale(1),
spaceDistance(150),
currentIndex(0),
startTime(0)
{}
CycleScroll* CycleScroll::create(Size &size, vector<Node*> nodes, float distance, float minScale /*= 1.0f*/)
{
auto ret = CycleScroll::create();
ret->nodes = nodes;
ret->spaceDistance = distance;
ret->minScale = minScale;
ret->disSize = size;
ret->initView();
ret->scheduleUpdate();
return ret;
}
void CycleScroll::initView()
{
this->setContentSize(this->disSize);
auto clipSize = this->disSize;
auto stencil = LayerColor::create(Color4B::RED, clipSize.width, clipSize.height);
clipNode = ClippingNode::create(stencil);
this->addChild(this->clipNode);
unsigned long cnt = this->nodes.size();
for(int i = 0; i< cnt; i++){
if(this->nodes[i] != NULL){
this->nodes[i]->setPosition(Vec2(this->nodes[i]->getContentSize().width/2 + i * spaceDistance, disSize.height/2));// TODO
clipNode->addChild(this->nodes[i]);
}
}
this->initListener();
// this->ScrollTo(0, 0);
}
void CycleScroll::setDisplaySize(const cocos2d::Size &size){
LayerColor::setContentSize(size);
clipNode->getStencil()->setContentSize(size);
}
const Size& CycleScroll::getDisplaySize() const{
return this->disSize;
}
void CycleScroll::ScrollTo(int index, float delay){
if(index < 0 || index >= this->nodes.size())
return;
Size contentSize = this->getContentSize();
float distance = contentSize.width/2 - this->nodes[index]->getPositionX();
for(auto i : this->nodes)
i->runAction(MoveBy::create(delay, Vec2(distance, 0)));
}
bool CycleScroll::init()
{
return LayerColor::initWithColor(Color4B(0,0,0,0));
}
void CycleScroll::initListener()
{
auto listener = EventListenerTouchOneByOne::create();
listener->onTouchBegan = CC_CALLBACK_2(CycleScroll::onTouchBegin, this);
listener->onTouchMoved = CC_CALLBACK_2(CycleScroll::onTouchMove, this);
listener->onTouchEnded = CC_CALLBACK_2(CycleScroll::onTouchEnd, this);
_eventDispatcher->addEventListenerWithSceneGraphPriority(listener, this);
}
bool CycleScroll::onTouchBegin(cocos2d::Touch *touch, cocos2d::Event *unused_event){
dragging = true;
scrollDistance = 0;
startTime = clock();
startPos = touch->getLocation();
return true;
}
void CycleScroll::onTouchMove(cocos2d::Touch *touch, cocos2d::Event *unused_event){
float disX = touch->getDelta().x;
this->updateNodePosX(disX);
}
void CycleScroll::onTouchEnd(cocos2d::Touch *touch, cocos2d::Event *unused_event){
// float startDistance = touch->getLocation().distance(touch->getStartLocation());
//
// if(startDistance < 10) // click
// {
//
// }
// 处理减速
dragging = false;
auto prePos = touch->getPreviousLocation();
auto curPos = touch->getLocation();
scrollDistance = curPos.distance(prePos);
int direction = curPos.x > startPos.x?1:-1;
scrollDistance = scrollDistance <= MAX_SCROLL_SPEED?scrollDistance : MAX_SCROLL_SPEED;
scrollDistance = scrollDistance * direction;
CCLOG("end:%f, pre:%f", curPos.x, prePos.x);
float longDisance = curPos.distance(startPos);
float elapsedTime = (clock() - startTime) / 1000; //毫秒
CCLOG("long:%f, t:%f", longDisance, elapsedTime);
if (elapsedTime < 50 && scrollDistance < 10 && longDisance >= 10){
scrollDistance = longDisance<=MAX_SCROLL_SPEED?longDisance:MAX_SCROLL_SPEED;
scrollDistance *= direction;
}
if (fabs(scrollDistance) > MIN_SCROLL_SPEED){
autoScrolling = true;
}
}
void CycleScroll::updateNodePosX(float interval){
int length = (int)nodes.size();
for (int i=0; i<length; i++){
auto node = this->nodes[i];
node->setPositionX(node->getPositionX() + interval);
}
}
void CycleScroll::deaccelerateScrolling(float dt){
if (dragging)
{
autoScrolling = false;
return;
}
CCLOG("sc:%f", scrollDistance);
scrollDistance *= 0.95;
if (fabs(scrollDistance) < MIN_SCROLL_SPEED){
autoScrolling = false;
startTime = 0;
}
this->updateNodePosX(scrollDistance);
}
void CycleScroll::update(float dt)
{
int length = (int)nodes.size();
float newPosX = 0;
auto s = 0.5;
auto mid = this->disSize.width/2;
for (int i=0; i<length; i++){
auto node = this->nodes[i];
auto curPosX = node->getPositionX();
if (curPosX < -node->getContentSize().width/2){
int beforeIndex = i-1;
beforeIndex = beforeIndex>=0?beforeIndex:length-1;
newPosX = nodes[beforeIndex]->getPositionX() + this->spaceDistance;
if (curPosX != newPosX){
node->setPositionX(newPosX);
curPosX = newPosX;
}
}else if (curPosX > this->disSize.width + node->getContentSize().width/2){
int afterIndex = i+1;
afterIndex = afterIndex<=length-1?afterIndex:0;
newPosX = nodes[afterIndex]->getPositionX() - this->spaceDistance;
if (curPosX != newPosX){
node->setPositionX(newPosX);
curPosX = newPosX;
}
}
if (curPosX <= mid){
s = curPosX / mid;
} else{
s = (this->disSize.width-curPosX) / mid;
}
s *= 1.2;
s = s<=1?s:1;
s = s>=this->minScale?s:this->minScale;
node->setScale(s);
}
if (autoScrolling){
this->deaccelerateScrolling(dt);
}
}