《Cocos Creator游戏实战》旋转循环页视图实现

旋转循环页视图实现

设置节点

编写代码


在这一节中我们实现一个类似旋转门的页视图(PageView)。

运行效果如下:

Cocos Creator版本:2.2.0

后台回复"旋转页视图",获取该项目完整文件:

设置节点

首先创建以下节点:

1. cardsNode为一个空节点,大小跟Canvas一样(我们之后会在该节点上设置触摸监听)。

2. card1-card6为cardNode的子节点,类型为Sprite,图片都在textures文件夹中:

现在我们把card1-card6节点的x坐标设置一下,将他们摆放在不同位置:

card1和card4是重叠的,其他card节点朝两边摆开。

注:因为节点顺序zIndex的关系,所以我们发现这里的遮挡关系其实不正确,之后需要在代码中调整。

编写代码

创建一个Revolve.js脚本文件,挂到cardsNode节点上,在onLoad方法中设置在各个位置上的card节点应有的属性:

// Revolve.js
onLoad () {
    this.attrArray = [
        JSON.stringify({
            zIndex: 3,
            scale: 1,
            opacity: 255,
            pos: this.node.children[0].position
        }),
        JSON.stringify({
            zIndex: 2,
            scale: 0.8,
            opacity: 180,
            pos: this.node.children[1].position
        }),
        JSON.stringify({
            zIndex: 1,
            scale: 0.5,
            opacity: 100,
            pos: this.node.children[2].position
        }),
        JSON.stringify({
            zIndex: 0,
            scale: 0.2,
            opacity: 30,
            pos: this.node.children[3].position
        }),
        JSON.stringify({
            zIndex: 1,
            scale: 0.5,
            opacity: 100,
            pos: this.node.children[4].position
        }),
        JSON.stringify({
            zIndex: 2,
            scale: 0.8,
            opacity: 180,
            pos: this.node.children[5].position
        }),
    ];
},

attrArray变量是一个数组,其中的元素都为JSON类型。

我们拿第一个元素来讲解:第一个元素指的是在正当中(C位),玩家当前正在观看的那张card节点(注意不一定是card1,因为玩家会滑动屏幕,card节点会更换)。

zIndex:此时该节点的zIndex值应该是最大的,不能被其他节点所遮挡。

scale和opacity:节点大小和透明度为原始状态,所以scale值和opacity值为1。

pos:因为我们最开始摆放时,card1节点在正当中,所以card1最开始的位置就是C位卡片应有的位置。由于card1节点位置会在玩家滑动后发生改变,所以我们这里需要调用JSON.stringfy方法对元素进行字符串化。

其余的元素代表其他位置上card节点应有的属性状态。

现在在onLoad方法中添加如下代码对各个节点进行初始化:

// Revolve.js
onLoad () {
    ...

    // 将cardsNode子节点的各个属性初始化
    this.cardsArray = this.node.children;
    for (let i=0; i<this.cardsArray.length; i++) {
        this.cardsArray[i].num = i;
        let initAttr = JSON.parse(this.attrArray[i]);
        this.cardsArray[i].zIndex = initAttr['zIndex'];
        this.cardsArray[i].scale = initAttr['scale'];
        this.cardsArray[i].opacity = initAttr['opacity'];
        // this.cardsArray[i].pos = initAttr['pos'];
    }
},

笔者这里给各个card节点加了一个num属性值,该值用来判断它们各自所处的位置,也就知道它们当前所应该呈现的属性状态。比如某节点num等于2的话,那么该节点的属性状态就对应为attrArray数组中的第3个元素。当然num值在玩家滑动后会发生改变。

运行后,效果如下:

接着我们要做的就是处理玩家滑动事件。在onLoad方法中添加触摸监听代码(只用监听touchmove就可以了):

// Revolve.js
onLoad () {
    ...
    
    // 触摸监听
    this.node.on('touchmove', this.onTouchMove, this);
},

onTouchMove方法编写如下:

// Revolve.js
onTouchMove (event) {
    let deltaX = event.getDelta().x;
    if (deltaX > this.node.parent.width / 40) {
        // 说明是往右滑动
        this.revolve2Right();
    }
    else if (deltaX < -this.node.parent.width / 40) {
        // 说明是向左滑动
        this.revolve2Left();
    }
},

通过判断玩家横向滑动的距离来执行相应代码(this.node.parent就是Canvas)。

revolve2Right方法编写如下:

// Revolve.js
revolve2Right () {
    // 往右旋转
    for (let i=0; i<this.cardsArray.length; i++) {
        // 所有节点编号+1
        if (this.cardsArray[i].num < this.cardsArray.length-1)
            this.cardsArray[i].num += 1;
        else
            this.cardsArray[i].num = 0;

        // 获取目标属性
        let nextAttr = JSON.parse(this.attrArray[this.cardsArray[i].num]);

        // 改变z值
        this.cardsArray[i].zIndex = nextAttr['zIndex'];
        
        // 改变大小、透明度、位置
        let scaleAction = cc.scaleTo(this.revolveTime, nextAttr['scale']);
        let fadeAction = cc.fadeTo(this.revolveTime, nextAttr['opacity']);
        let moveAction = cc.moveTo(this.revolveTime, nextAttr['pos']);
        let spawnAction = cc.spawn(scaleAction, fadeAction, moveAction);
        spawnAction.setTag(1);      // 设置动作标签
        this.cardsArray[i].runAction(spawnAction);
    } 
}

如果向右滑动的话,那么所有card节点的num值加1,但注意num值最大如果超过cardsArray数组长度减1的话,就要归0(不然就超过数组下标临界值,获取不到元素了)。设置好num值后,我们就可以设置各个card节点的zIndex,scale,opacity和pos值了。revolveTime在properties中创建:

properties: {
    revolveTime: 0.8            // 旋转所需时间
},

大家可以发现笔者给spawnAction设置了一个tag,这个稍后会讲到。

rovolve2Left方法同理:

// Revolve.js
revolve2Left () {
    // 往左旋转
    for (let i=0; i<this.cardsArray.length; i++) {
        // 所有节点编号-1
        if (this.cardsArray[i].num > 0)
            this.cardsArray[i].num -= 1;
        else
            this.cardsArray[i].num = this.cardsArray.length-1;

        // 获取目标属性
        let nextAttr = JSON.parse(this.attrArray[this.cardsArray[i].num]);

        // 改变z值
        this.cardsArray[i].zIndex = nextAttr['zIndex'];
        
        // 改变大小、透明度、位置
        let scaleAction = cc.scaleTo(this.revolveTime, nextAttr['scale']);
        let fadeAction = cc.fadeTo(this.revolveTime, nextAttr['opacity']);
        let moveAction = cc.moveTo(this.revolveTime, nextAttr['pos']);
        let spawnAction = cc.spawn(scaleAction, fadeAction, moveAction);
        spawnAction.setTag(1);      // 设置动作标签
        this.cardsArray[i].runAction(spawnAction);
    }   
},

现在大家运行项目,滑动后发现界面变成了下面这个混乱的样子:

那是因为我们每次滑动,onTouchMove方法都被调用了好几次,而每次调用时各个card节点都会执行相应动作。问题是之前的动作都还没执行完毕,新的执行就又来了,所以才会导致以上情况出现。

解决办法就是在onTouchMove中先判断动作是否执行完毕。如果不是的话,直接return。

// Revolve.js
onTouchMove (event) {
    // 首先判断动作是否执行完
    for (let i=0; i<this.cardsArray.length; i++) {
        if (this.cardsArray[i].getActionByTag(1))
            return;
    }

    let deltaX = event.getDelta().x;
    if (deltaX > this.node.parent.width / 40) {
        // 说明是往右滑动
        this.revolve2Right();
    }
    else if (deltaX < -this.node.parent.width / 40) {
        // 说明是向左滑动
        this.revolve2Left();
    }
},

这里的重点就是getActionByTag方法。我们在revolve2Right和revolve2Left方法中给spawnAction设置了一个标签,如果spawnAction没有在执行(或者执行完毕),那么getActionByTag(1)就会返回null;而如果正在执行的话,就会返回一个object。

现在运行项目,旋转就正常了:

好,那今天的教程就到这,希望大家有所收获~

  • 6
    点赞
  • 21
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 1
    评论
源码KIPageView,UITableView 很强大,可是只能竖向滚动;UICollectionView 可以解决各种布局难题,但是稍显复杂,对于一些简单的需求,有点杀鸡用牛刀的感觉。 在 iOS6 以前,还没有 UICollectionView,为了实现横向滚动的 UITableView,只有自己动手写组件。为了达到和 UITableView 差不多的效果,就得先弄清其内部实现机制是怎么回事。 在渲染 View 的时候,是很耗系统资源的,如果创建大量的 View, 系统运行将变得异常缓慢,甚至导致内存耗尽。但是,在实际应用中,我们难免会遇到大量的数据需要显示,如果每显示一个数据,我们都创建一个 View,那应用程序的体验将相当糟糕。所以 Apple 为 iOS 开发者提供了 UITableView,Google 为 Android 开发者提供了 ListView。 简单来讲,UITableView 采用复用机制,其只会显示其可见区域内的 UITableViewCell。我们在滑动的过程中,当超出 UITableView 可见区域的 Cell,将会从 UITableView 中移除,并加入回收池中以作复用。当 UITableView 需要显示新的 Cell,会先从回收池中查找是否有相应的 Cell 可以重用(通过 dequeueReusableCellWithIdentifier:)。如果有,则直接将其重新显示;如果没有,则创建新的 Cell。这样一来,就可以避免因创建过多的 View,导致内存耗尽的尴尬情况。 了解了其内部的运行原理,我们也可以实现一个自己的 UITableView。 很常见的一个应用场景——显示图片:如果显示一张图片,我们用一个 UIImageView 足矣,如果要显示多张图片,并且可以左右滚动,最简单的办法是用一个 UIScrollView 包含多个 UIImageView, 但是这样带来的后果则是,如果图片数据量较大,那这个程序根本没有办法正常使用。如果我们还需要实现无限循环滚动,那这个解决方案肯定是不行的。所以这时候,就得我们自己实现一个 UITableView。 最开始,我写了一个组件叫 KIFlowView实现了上面讲的需求,但是都是 iOS5 时代的产物了,难免过于陈旧。在后续的工作中也发现,类似的需求其实挺多的,比如左右滑动的 View,如网易新闻客户端,可以左右滑动,在不同的新闻栏目之间进行切换;有时候我们也需要实现一些 Tab,如果 Tab 的项目比较多,也需要考虑复用的问题,所以决定重新写一个增强组件,作为其替代品,所以就产生了 KIPageView。 测试环境:Xcode 6.2,iOS 6.0 以上

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

la_vie_est_belle

谢谢支持

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值