使用引擎:cocos2d-js 3.0

使用语言:javascript

运行平台:手机web

---------------------------------------------------------------

初步分析:

操作过程:

滑动,转动,放缓速度,平衡,停止。

时间选择器的功能分析:

1. 滑动

2. N个选项结果(eg. 0~10)

3. 部分数字可见

4. 根据最近原则,自动平衡到结果item

5. ...

--------------------------------------------------------------------

于是我们立刻面临两种选择,一种是继承ScrollView的做法,一种是使用ClippingNode自己造轮子。

我们如果选择继承的做法,那么无疑工作量会比较多(修改ScrollView逻辑流程)。

其实循环滚动的逻辑一点也不复杂,我们没必要去使用任何已有的滑动类控件,无论是扩展性还是运行效率,这个轮子自己造才是最好的选择。

那么我们使用ClippingNode?

不是!而是使用ccui::Layout。最终我选择它的原因是,它不需要设置stencil,直接一句话即可设置成为裁剪(setClippingEnabled),减少子父节点坐标计算的复杂性。


--------

UI如下:

--------


--------

核心点:

--------

1. 鉴于效率和方便性,使用一个contentNode来管理所有item。

2. 通过_bMoveing和_bTouching标识来处理触摸和移动之间相互影响。

3. 循环滚动的实现逻辑:向上移动,那么最上的item会移动到最下,向下则反之。

4. 在操作结束之后,做矫正平衡(准确移动到固定的位置)。


--------

代码实现:

--------

/**
 * Created by xxf on 12/19/2014.
 *
 *  1. touchTime < 0.3 s 判定为加速
 *  2. 平衡性判断
 *  3. 平衡后停止
 *  
 *
 *  eg:
 *
 *   var list = ["1","2","3","4","5","6","7","8","9","10"];
     var deFinition = {
                size:cc.size(60,125),items:list,backGround: res.back
            };
     var node = new ScrollSelector(deFinition);
 */

var ScrollSelector = ccui.Layout.extend({
    _visibleClipNumber:3,// 同时显示Item的个数
    _veLocity:3,// 速率
    _timeLimit:0.5,// 时间限制
    _distanceLimit:500,// 距离限制
    _timeCondition:0.3,// 时间条件
    _distanceCondition:30,// 距离条件

    _diffY:0,_diffYCount:0,// 周期性计数,y轴的移动距离
    _onceDiffYCount:0,// 一次触摸y轴的移动距离
    _timeCount:0,// 触摸时间计时
    _runningAction:null,_list:null,// item列表,用来平衡坐标
    _originList:null,_currentItemIndex:2,// 目前是第几个item
    _value:null,// 当前item的value
    _beginPos:null,_contentNode:null,_backGround:null,_bMoveing:false,_bTouching:false,_bBeginCountTime:false,_fontColor: cc.color.BLACK,_fontSize: 40,_fontname: "Arial",// _mode:null,//游戏类型:选择器,游戏器

    ctor: function (params) { //params 参数列表: items,textures.back,size
        this._super();
        this.setClippingEnabled(true);
        this.setContentSize(params.size);
        this.setAnchorPoint(cc.p(0.5,0));
        this.ignoreAnchor = false;
        this._backGround = new cc.Sprite(params.backGround);
        this.addChild(this._backGround);
        this._backGround.x = this.width/2;
        this._backGround.y = this.height/2;
        this._contentNode = new cc.Node();
        this._contentNode.setAnchorPoint(cc.p(0.5,0));
        this.addChild(this._contentNode);
        this._contentNode.setPosition(cc.p(this.width/2,0));

        // 创建N个Item
        this._list = [];
        this._originList = [];

        for(var i = 0; i < params.items.length; i++){
            var text = ccui.Text.create(params.items[i],this._fontName,this._fontSize);
            text.setTextVerticalAlignment(cc.VERTICAL_TEXT_ALIGNMENT_CENTER);
            text.getVirtualRenderer().setColor(this._fontColor);
            text.setTextAreaSize(cc.size(params.size.width,params.size.height/3));
            text.setPosition(cc.p(0,i* text.height + text.height/2));
            this._contentNode.addChild(text,1,i);
            this._list.push(text);
            this._originList.push(text);
        }
        this._value = this._originList[1].getString();

        this._lister = cc.EventListener.create({
            swallowtouches: true,event: cc.EventListener.TOUCH_ONE_BY_ONE,onTouchBegan:this._onTouchBegan,onTouchMoved:this._onTouchMoved,onTouchEnded:this._onTouchEnded
        });
        cc.eventManager.addListener(this._lister,this);
        this.scheduleUpdateWithPriority(0);

        // init data
        this._diffYCount = this._list[0].height;
        this._beginPos = cc.p(0,0);
        this._distanceLimit = params.size.height*2 + Math.random()*50;

    },update: function(dt){
        if(!this._bTouching && this._bMoveing){ // Action中的时候,计算偏移量
            var diffY = this._contentNode.y - this._beginPos.y;
            this._diffYCount = this._diffYCount + diffY;
            this._beginPos = this._contentNode.getPosition();
        }
        this._balance();
        if(this._bBeginCountTime) this._timeCount = this._timeCount + dt;
    },_onTouchBegan:function (touch,event) {
        var target = event.getCurrentTarget();
        if (!target.containsTouchLocation(touch)) return false;
        target._beginPos = touch.getLocation();
        target._bTouching = true;
        target._bMoveing = false;
        if (target._contentNode.isRunning())
            target._contentNode.stopAction(target._runningAction);
        // 开启滑动计时
        target._bBeginCountTime = true;
        target._timeCount = 0;
        return true;
    },_onTouchMoved:function (touch,event) {
        // Move中的时候,计算偏移量
        var target = event.getCurrentTarget();
        var getPoint = touch.getLocation();
        var diffY = getPoint.y - target._beginPos.y;
        target._contentNode.y = target._contentNode.y + diffY;
        target._beginPos = getPoint;
        target._diffYCount = target._diffYCount + diffY;
        target._onceDiffYCount = target._onceDiffYCount + diffY;
    },_onTouchEnded:function (touch,event) {
        var target = event.getCurrentTarget();
        target._bTouching = false;

        // 计算滑动  计算距离  计算速度
        if (/*Math.abs(target._onceDiffYCount) > target._distanceCondition &&*/ target._timeCount < target._timeCondition) {
            if (target._contentNode.isRunning())  target._contentNode.stopAction(target._runningAction);
            var distance = Math.round(target._onceDiffYCount*target._veLocity);
            var time = target._timeCount * target._veLocity;
            var pn = distance > 0 ? 1 : -1;
            distance = Math.abs(distance) > Math.abs(target._distanceLimit) ? pn * target._distanceLimit: distance;
            time = time < target._timeLimit ? target._timeLimit: time;
            var move = cc.moveBy(time,distance);
            target._runningAction = cc.sequence(move.easing(cc.easeSineOut()),cc.callFunc(target._bounceBalance,target));
            target._runningAction = target._contentNode.runAction(target._runningAction);
            target._beginPos = target._contentNode.getPosition();
            target._bMoveing = true;
        }else{ // 如果不移动,那么直接做平衡
            target._bounceBalance();
        }
        target._onceDiffYCount = 0;
        target._timeCount = 0;
        target._bBeginCountTime = false;
    },containsTouchLocation:function (touch) {
        var getPoint = touch.getLocation();
        var myRect = this.getBoundingBox();
        return cc.rectContainsPoint(myRect,getPoint);
    },_balance:function(){
        if(this._diffYCount > this._list[0].height){
            var topItem = this._list.pop();
            topItem.y = this._list[0].y - this._list[0].height;
            this._list.unshift(topItem);
            this._diffYCount = this._diffYCount - this._list[0].height;
        }else if (this._diffYCount < -this._list[0].height) {
            var bottomItem = this._list.shift();
            bottomItem.y = this._list[this._list.length-1].y + this._list[this._list.length-1].height;
            this._list.push(bottomItem);
            this._diffYCount = this._diffYCount + this._list[0].height;
        }
    },_bounceBalance:function(){
        var itemHight = this._list[0].height;
        var num = Math.round(this._contentNode.y%itemHight);
        var distance = 0;
        if ( num > 0){
            distance = num > itemHight/2 ? itemHight-num : -num;
        }else {
            distance = num > -itemHight/2 ? -num : -(itemHight + num);
        }
        var action = cc.moveBy(0.2,distance).easing(cc.easeSineOut());
        this._contentNode.runAction(cc.sequence(action,cc.callFunc(this._end,this)));
    },_end: function(){
        var num = Math.round(this._contentNode.y/this._list[0].height);
        var num2 = -1 * (num % this._list.length);
        if (num2 > 0){
            this._currentItemIndex = num2 + 2;
        }else if (num2 < 0){
            this._currentItemIndex = this._list.length + num2 + 2;

        }else{
            this._currentItemIndex = 2;
        }
        if (this._currentItemIndex > this._list.length){
            this._currentItemIndex = this._currentItemIndex % this._list.length;
        }
        this._value = this._originList[this._currentItemIndex-1].getString();
        cc.log(this._value);
    }

});


----------------------------------

由于设置了每秒滑动距离的限制,因此代码在手机web平台几乎没有效率问题。

在Native平台可以放宽一点,让滑动更爽快~

最后,由于小弟知识水平有限,代码中有各种不规范的地方望各位大神们多多包含。

Cocos2d-js : 模拟IOS时间选择器的更多相关文章

  1. Html5 滚动穿透的方法

    这篇文章主要介绍了Html5 滚动穿透的方法,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧

  2. HTML5 拖放(Drag 和 Drop)详解与实例代码

    本篇文章主要介绍了HTML5 拖放(Drag 和 Drop)详解与实例代码,具有一定的参考价值,有兴趣的可以了解一下

  3. 跨域修改iframe页面内容详解

    这篇文章主要介绍了跨域修改iframe页面内容详解,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧

  4. ios – Xcode找不到Alamofire,错误:没有这样的模块’Alamofire’

    我正在尝试按照github(https://github.com/Alamofire/Alamofire#cocoapods)指令将Alamofire包含在我的Swift项目中.我创建了一个新项目,导航到项目目录并运行此命令sudogeminstallcocoapods.然后我面临以下错误:搜索后我设法通过运行此命令安装cocoapodssudogeminstall-n/usr/local/bin

  5. ios – 暂停调度队列是否会暂停其目标队列?

    我想创建两个串行队列A&B.队列B是队列A的目标.我想在B上排队一些块,并暂停它直到我准备执行它们,但是我想继续在队列A上执行块.如果我暂停B,这还会暂停它的目标队列(队列A)吗?我的想法是,我想安排这些特定的块在稍后日期执行但是我不希望它们同时运行而我不这样做想要处理信号量.但我希望队列A继续处理它的块,而B则被暂停如果不清楚这里是一些示例代码解决方法queueB被挂起,但queueA未被挂起.queueA和queueB被挂起.

  6. ios – 使用CocoaPods post install hook将自定义路径添加到HEADER_SEARCH_PATHS

    解决方法在Podfile中定义一个方法:然后在post_install中调用该方法:

  7. ios – 在Swift中删除WKWebView Accesory栏

    我现在正试着将this转换成Swift而没有真正的背景.这是我到目前为止所得到的…而且我一直在寻找谷歌并不知道要搜索什么才能更具体.你能否详细说明我做错了什么?

  8. iOS,ld:framework没有找到适用于架构arm64的GoogleMaps

    Podfile看起来像这个Cocoapodsv1.0beta6):解决方法更新请检查您是否在架构中具有相同的构建设置,并仅构建活动体系结构中的目标键你的podfile应该是这样的在您启动ProjectTest目标之前结束项目目标,也是为什么添加继承!

  9. ios – 为CocoaPods的pod设置部署目标

    我使用CocoaPods来管理项目中的依赖关系.我写了Podfile:此文件与CocoaPods0.x配合使用,但在我更新到CocoaPods1.0之后,我无法编译项目.运行后我无法编译我的项目错误:/Users/

  10. ios – 如何在故事板上单击UIImageView(swift)

    我是新来的,我想知道如何在故事板上单击ImageView.我想要做的是使其点击时,它显示另一个视图控制器.解决方法您可以添加tapGesture.这是代码:Swift3.0

随机推荐

  1. 【cocos2d-x 3.x 学习笔记】对象内存管理

    Cocos2d-x的内存管理cocos2d-x中使用的是上面的引用计数来管理内存,但是又增加了一些自己的特色。cocos2d-x中通过Ref类来实现引用计数,所有需要实现内存自动回收的类都应该继承自Ref类。下面是Ref类的定义:在cocos2d-x中创建对象通常有两种方式:这两中方式的差异可以参见我另一篇博文“对象创建方式讨论”。在cocos2d-x中提倡使用第二种方式,为了避免误用第一种方式,一般将构造函数设为protected或private。参考资料:[1]cocos2d-x高级开发教程2.3节[

  2. 利用cocos2dx 3.2开发消灭星星六如何在cocos2dx中显示中文

    由于编码的不同,在cocos2dx中的Label控件中如果放入中文字,往往会出现乱码。为了方便使用,我把这个从文档中获取中文字的方法放在一个头文件里面Chinese.h这里的tex_vec是cocos2dx提供的一个保存文档内容的一个容器。这里给出ChineseWords,xml的格式再看看ChineseWord的实现Chinese.cpp就这样,以后在需要用到中文字的地方,就先include这个头文件然后调用ChineseWord函数,获取一串中文字符串。

  3. 利用cocos2dx 3.2开发消灭星星七关于星星的算法

    在前面,我们已经在GameLayer中利用随机数初始化了一个StarMatrix,如果还不知道怎么创建星星矩阵请回去看看而且我们也讲了整个游戏的触摸事件的派发了。

  4. cocos2dx3.x 新手打包APK注意事项!

    这个在编译的时候就可以发现了比较好弄这只是我遇到的,其他的以后遇到再补充吧。。。以前被这两个问题坑了好久

  5. 利用cocos2dx 3.2开发消灭星星八游戏的结束判断与数据控制

    如果你看完之前的,那么你基本已经拥有一个消灭星星游戏的雏形。开始把剩下的两两互不相连的星星消去。那么如何判断是GameOver还是进入下一关呢。。其实游戏数据贯穿整个游戏,包括星星消除的时候要加到获得分数上,消去剩下两两不相连的星星的时候的加分政策等,因此如果前面没有做这一块的,最好回去搞一搞。

  6. 利用cocos2dx 3.2开发消灭星星九为游戏添加一些特效

    needClear是一个flag,当游戏判断不能再继续后,这个flag变为true,开始消除剩下的星星clearSumTime是一个累加器ONE_CLEAR_TIME就是每颗星星消除的时间2.连击加分信息一般消除一次星星都会有连击信息和加多少分的信息。其实这些combo标签就是一张图片,也是通过控制其属性或者runAction来实现。源码ComboEffect.hComboEffect.cpp4.消除星星粒子效果消除星星时,为了实现星星爆裂散落的效果,使用了cocos2d提供的粒子特效引擎对于粒子特效不了

  7. 02 Cocos2D-x引擎win7环境搭建及创建项目

    官网有搭建的文章,直接转载记录。环境搭建:本文介绍如何搭建Cocos2d-x3.2版本的开发环境。项目创建:一、通过命令创建项目前面搭建好环境后,怎样创建自己的Cocos2d-x项目呢?先来看看Cocos2d-x3.2的目录吧这就是Cocos2d-x3.2的目录。输入cocosnew项目名–p包名–lcpp–d路径回车就创建成功了例如:成功后,找到这个项目打开proj.win32目录下的Hello.slnF5成功了。

  8. 利用cocos2dx 3.2开发消灭星星十为游戏添加音效项目源码分享

    一个游戏,声音也是非常的重要,其实cocos2dx里面的简单音效引擎的使用是非常简单的。我这里只不过是用一个类对所有的音效进行管理罢了。Audio.hAudio.cpp好了,本系列教程到此结束,第一次写教程如有不对请见谅或指教,谢谢大家。最后附上整个项目的源代码点击打开链接

  9. 03 Helloworld

    程序都有一个入口点,在C++就是main函数了,打开main.cpp,代码如下:123456789101112131415161718#include"main.h"#include"AppDelegate.h"#include"cocos2d.h"USING_NS_CC;intAPIENTRY_tWinMain{UNREFERENCED_ParaMETER;UNREFERENCED_ParaMETER;//createtheapplicationinstanceAppDelegateapp;return

  10. MenuItemImage*图标菜单创建注意事项

    学习cocos2dx,看的是cocos2d-x3.x手游开发实例详解,这本书错误一大把,本着探索求知勇于发现错误改正错误的精神,我跟着书上的例子一起调试,当学习到场景切换这个小节的时候,出了个错误,卡了我好几个小时。

返回
顶部