(二)Cocos2d-JS制作一个微信报名宣传页——框架实现

发表于2015-08-21
评论0 622浏览

接下来,我们要开始思考,如何填充我们的主要功能——5个页面的框架。

在这里我们使用cc.Layer.extend定义出5个Layer,并在每个layer中,实现appear方法和disappear方法,分别用于控制页面的入场动画和出场动画,后面,我们会详细介绍每个layer的动画,现在我们设计出layer的框架后呢,我们就可以动手编写MainScene里面的initUI的剩余部分和nextPage函数的编写了,一下是完整的initUI()代码:

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
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
initUI: function () {
    var bg = new cc.Sprite(res.background_png);
    bg.anchorX = 0;
    bg.anchorY = 0;
    bg.scaleX = cc.winSize.width / bg.width;
    bg.scaleY = cc.winSize.height / bg.height;
    this.addChild(bg, 0);
    this.arrow = new cc.Sprite("#arrow.png");
    this.arrow.setPosition(cc.pAdd(cc.visibleRect.bottom, cc.p(0, 50)));
    var posY = this.arrow.y;
    var arrowAction = cc.repeatForever(cc.sequence(cc.spawn(cc.moveTo(0.8, cc.p(this.arrow.x, posY + 30)).easing(cc.easeIn(0.5)), cc.fadeOut(1)), cc.delayTime(0.8), cc.callFunc(function () {
        this.arrow.y = this.arrow.y - 30;
        this.arrow.opacity = 255;
    }, this)));
    this.arrow.runAction(arrowAction);
    this.addChild(this.arrow, 1);
    this.menuItemToggle = new cc.MenuItemToggle(new cc.MenuItemImage("#music.png"), new cc.MenuItemImage("#music_sel.png"), this.toggleMusicCallback, this);
    this.menuItemToggle.setPosition(cc.pAdd(cc.visibleRect.right, cc.p(-this.menuItemToggle.width / 2 - 30, 140)));
    var togglemenu = new cc.Menu(this.menuItemToggle);
    togglemenu.anchorX = 0;
    togglemenu.anchorY = 0;
    togglemenu.x = 0;
    togglemenu.y = 0;
    this.addChild(togglemenu, 1);
    this.animLayer = new cc.Layer();
    this.addChild(this.animLayer);
    this.sceneList.push(new Layer1());
    this.sceneList.push(new Layer2());
    this.sceneList.push(new Layer3());
    this.sceneList.push(new Layer4());
    this.sceneList.push(new Layer5());
    for (var i = 0; i < this.sceneList.length; i++) {
        var scene = this.sceneList;
        scene.anchorX = 0;
        scene.anchorY = 0;
        scene.x = 0;
        scene.y = 0;
        if (this.currentIndex != i) {
            scene.setVisible(false);
        }
        this.animLayer.addChild(scene, this.sceneList.length - i);
    }
},

可以看到,首先,我们在MainScene中定义了一个animLayer,用来控制各个页面的动画层,然后我们定义了一个数组用来存储我们的需要的5个Layer,最后他们添加到animLayer里面,并将非当前显示页的Layer隐藏,这样引擎就不会去绘制他们了。

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
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
addTouch: function () {
    var self = this;
    self.listener = cc.EventListener.create({
        event: cc.EventListener.TOUCH_ONE_BY_ONE,
        swallowTouches: true,
        startPosY: 0,
        onTouchBegan: function (touch, event) {
            this.startPosY = touch.getLocation().y;
            return true;
        },
        onTouchMoved: function (touch, event) {
        },
        onTouchEnded: function (touch, event) {
            if (musicPlayStatus) {
                playMusic(true);
            }
            if (canChangePage) {
                var delta = touch.getLocation().y - this.startPosY;
                if (delta > 15 && self.currentIndex < self.sceneList.length - 1) {
                    self.changePage(++self.currentIndex, true);
                else if (delta < -15 && self.currentIndex > 0) {
                    self.changePage(--self.currentIndex, false);
                }
            }
        },
        onTouchCancelled: function (touch, event) {
        }
    });
    cc.eventManager.addListener(self.listener, self);
},
changePage: function (index, next) {
    canChangePage = false;
    var scene = next ? this.sceneList[index - 1] : this.sceneList[index + 1];
    if (index == 4) {
        this.togleArrow(false);
    else {
        this.togleArrow(true);
    }
    var nextPage = function () {
        scene.visible = false;
        this.sceneList[index].visible = true;
        this.sceneList[index].appear();
    };
    if (scene) {
        scene.disappear(nextPage, this);
    }
}

我们想好如何去编写5个页面后,就可以开始编写addTouch()和changePage()了,在这里,我们设置了一个全局变量canChangePage来判断当前换页的操作是否可以执行,如果动画正在播放中,我们不能进行页面的切换操作。由于iOS和某些Android特定版本的限制,需要有实际的点击才能触发音频的播放,所以我们在onTouchEnded的时候,去触发音频播放。


说到音频播放,我们有2个选择,第一可以使用引擎自带的cc.audioEngine,其中集成了多种浏览器的兼容播放,可以很省时省力的进行音乐播放,不过这里,由于我们比较在意code的体积,所以直接使用简单粗暴的html标签<audio></audio>以下是我们简单实现的音乐播放,使用一个全局变量musicPlayStatus来控制音频的播放状态,其实现如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
var initMusic = function () {
    var audio = getById("myAudio");
    audio.src = "res/bg.mp3";
}
var playMusic = function (status) {
    var audio = getById("myAudio");
    if (status) {
        if (audio.paused) {
            audio.play();
            musicPlayStatus = true;
        }
    else {
        if (!audio.paused) {
            audio.pause();
            musicPlayStatus = false;
        }
    }
}

由于我们不希望音乐在开始加载页面的时候就进行下载,所以我们使用initMusic()这个方法来初始化音乐,并使用playMusic()的方法来控制音乐的播放。

同时我们需要在MainScene加入一个方法,用来监听浏览器进入后台,和从后台进入前台的事件,用来控制音乐的播放状态,引擎已经封装好了对应的事件,我们可以非常方便的去监听这些事件,可以大大减少开发的周期。

1
2
3
4
5
6
7
8
initHideEvent: function () {
    cc.eventManager.addCustomListener(cc.game.EVENT_SHOW, function () {
        playMusic(true);
    });
    cc.eventManager.addCustomListener(cc.game.EVENT_HIDE, function () {
        playMusic(false);
    });
}


第一个页面的实现

由于我们已经制定好了,页面的规则,所以每个页面我们要做的就以下几点:

  • 实现UI的布局;

  • 实现appear入场动画;

  • 实现disappear出场动画;

  • 控制能否滑动到下一个屏幕的状态;

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
29
30
31
32
33
34
35
initUI: function () {
    canChangePage = false;
    this.accLayer = new cc.Layer();
    this.accLayer.anchorX = 0;
    this.accLayer.anchorY = 0;
    this.accLayer.x = 0;
    this.accLayer.y = 0;
    this.addChild(this.accLayer);
    this.logo = new cc.Sprite("#logo.png");
    this.logo.scale = 0;
    this.logo.setPosition(cc.pAdd(cc.visibleRect.center, cc.p(0, this.logo.height / 3 * 2)));
    this.addChild(this.logo);
    this.leftFont = new cc.Sprite("#font_left.png");
    this.leftFont.setPosition(cc.pAdd(cc.visibleRect.left, cc.p(-this.leftFont.width / 2, -this.leftFont.height / 2)));
    this.addChild(this.leftFont);
    this.rightFont = new cc.Sprite("#font_right.png");
    this.rightFont.setPosition(cc.pAdd(cc.visibleRect.right, cc.p(this.rightFont.width / 2, -this.rightFont.height / 2)));
    this.addChild(this.rightFont);
    this.leftUpPic = new cc.Sprite("#block_left up.png");
    this.leftUpPic.anchorX = 0, this.leftUpPic.anchorY = 1;
    this.leftUpPic.setPosition(cc.pAdd(cc.visibleRect.topLeft, cc.p(-this.leftUpPic.width, this.leftUpPic.height)));
    this.accLayer.addChild(this.leftUpPic);
    this.leftDownPic = new cc.Sprite("#block_left down.png");
    this.leftDownPic.anchorX = 0, this.leftDownPic.anchorY = 0;
    this.leftDownPic.setPosition(cc.pAdd(cc.visibleRect.bottomLeft, cc.p(-this.leftDownPic.width + 20, -this.leftDownPic.height)));
    this.accLayer.addChild(this.leftDownPic);
    this.rightUpPic = new cc.Sprite("#block_right up.png");
    this.rightUpPic.anchorX = 1, this.rightUpPic.anchorY = 1;
    this.rightUpPic.setPosition(cc.pAdd(cc.visibleRect.topRight, cc.p(this.rightUpPic.width, this.rightUpPic.height)));
    this.accLayer.addChild(this.rightUpPic);
    this.rightDownPic = new cc.Sprite("#block_right down.png");
    this.rightDownPic.anchorX = 1, this.rightDownPic.anchorY = 0;
    this.rightDownPic.setPosition(cc.pAdd(cc.visibleRect.bottomRight, cc.p(this.rightDownPic.width, -this.rightDownPic.height)));
    this.accLayer.addChild(this.rightDownPic);
}

以上是对第一个页面的布局,从图上我们其实就可以看到,页面由logo、左上、左下、右上、右下、和左右两列文字组成,至于布局我们就不多说了,主要有以下几点。

  • 可以看到我们依然使用相对布局,让每个ui可以适应不同屏幕分辨率

  • 由于有入场动画,所以我们将这些UI都放到屏幕外面了

  • 我们对三角形的物体,放置在了一个独立的Layer -- accLayer,为的是以后如果需要添加陀螺仪的监听,

我们可以很方便的进行修改。

接下来我们要实现的时入场动画

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
appear: function () {
    var logoAction = cc.sequence(cc.scaleTo(0.5, 1), cc.callFunc(function () {
        this.leftUpPic.leftUpAction = cc.moveTo(0.5, cc.p(this.leftUpPic.x + this.leftUpPic.width, this.leftUpPic.y - this.leftUpPic.height + 80));
        this.leftUpPic.runAction(this.leftUpPic.leftUpAction);
        this.leftDownPic.leftDownAction = cc.moveTo(0.5, cc.p(this.leftDownPic.x + this.leftDownPic.width, this.leftDownPic.y + this.leftDownPic.height));
        this.leftDownPic.runAction(this.leftDownPic.leftDownAction);
        this.rightUpPic.rightUpAction = cc.moveTo(0.5, cc.p(this.rightUpPic.x - this.rightUpPic.width - 30, this.rightUpPic.y - this.rightUpPic.height - 30));
        this.rightUpPic.runAction(this.rightUpPic.rightUpAction);
        this.rightDownPic.rightDownAction = cc.moveTo(0.5, cc.p(this.rightDownPic.x - this.rightDownPic.width, this.rightDownPic.y + this.rightDownPic.height));
        this.rightDownPic.runAction(this.rightDownPic.rightDownAction);
        this.leftFont.fontLeftAction = cc.sequence(cc.delayTime(0.3), cc.moveTo(0.5, cc.p(this.leftFont.x + this.leftFont.width, this.leftFont.y)));
        this.leftFont.runAction(this.leftFont.fontLeftAction);
        this.rightFont.fontRightAction = cc.sequence(cc.delayTime(0.4), cc.moveTo(0.4, cc.p(this.rightFont.x - this.rightFont.width, this.rightFont.y)));
        this.rightFont.runAction(this.rightFont.fontRightAction);
    }, this), cc.delayTime(1.3), cc.callFunc(function () {
        canChangePage = true;
    }, this));
    this.logo.runAction(logoAction);
}

appear的实现方式非常简单,其实就是把几个我们上面写好的UI,移动到我们希望的位置。

logoAction,使用前面介绍过的函数cc.sequence()和cc.scaleTo(),最后在结束的时候调用cc.callFunc()。

在cc.callFunc()中呢,我们对其余的元素一一进行了入场动画,主要使用了cc.moveTo()这个方法,可以看到,我们从屏幕外面,移动到了屏幕里面。

值得注意的是,我们在logoAction的最后,使用cc.delayTime()延时了一段时间,然后再执行canChangePage = true主要是为了在动画播放过程中,不然用户滑动改变页面。

接下来就是disappear方法的实现了:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
disappear: function (callback, target) {
    var action = cc.sequence(cc.scaleTo(0.5, 0), cc.callFunc(function () {
        this.leftUpPic.runAction(this.leftUpPic.leftUpAction.reverse());
        this.leftDownPic.runAction(this.leftDownPic.leftDownAction.reverse());
        this.rightUpPic.runAction(this.rightUpPic.rightUpAction.reverse());
        this.rightDownPic.runAction(this.rightDownPic.rightDownAction.reverse());
        this.leftFont.runAction(this.leftFont.fontLeftAction.reverse());
        this.rightFont.runAction(this.rightFont.fontRightAction.reverse());
    }, this), cc.delayTime(0.9), cc.callFunc(function () {
        if (target && callback) {
            callback.call(target);
        }
    }, this));
    this.logo.runAction(action);
}

很简单,我们也是让logo执行了一个action,然后再执行disappear传进来的回调方法就ok了。

这里,我们使用了action.reverse()这个方法,可以很简便的实现大多数action的reverse。

动画播放完毕后,调用回调方法执行我们需要的回调操作。

ok,此时我们第一个页面已经编写完了,赶紧运行下试试吧。接下来,由于第二第四第五个页面跟第一个页面都很类似,我们就不重复讲了,大家看看代码吧,也当做练练手。我们接着重点说说第三个页面的实现。


第三个页面的实现

第三个页面,是区别于其他页面的一个页面,主要原因是其余的页面,我们基本使用的是cc.moveTo()这个方法,配合上cc.delayTime()等,实现一系列的移动,只要多试验几次,我们就可以很清楚这些action的使用方法,各种混搭之后,可以实现出很复杂也很绚丽的效果。

不过接下来我们将介绍一个之前没有用过的接口cc.progressTimer()来快速实现我们的一条往下滑动的效果。

cc.progressTimer() 多数情况用于使用某个sprite进行计时,倒计时操作,如下图,我们可以很清楚的看到他的效果,我们使用这个效果,可以很容易的实现出那个功能。

59_362141_e63676164a3684a.gif

我们需要准备一张图片,如下图,然后使用progressTimer,就可以很容易实现这个功能了。

59_362141_463ad95961ea6b2.png

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
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
initUI: function () {
    ...
    this.line = new cc.ProgressTimer(new cc.Sprite(res.line_png));
    this.line.type = cc.ProgressTimer.TYPE_BAR;
    this.line.midPoint = cc.p(0, 1);
    this.line.barChangeRate = cc.p(0, 1);
    this.line.percentage = 0;
    this.line.setPosition(cc.p(cc.winSize.width / 2 - 30, cc.winSize.height - this.line.height / 2));
    this.addChild(this.line);
     
    ...
},
appear: function () {//20,50,80,100
    this.leftUpPic.inAciton = cc.moveTo(0.2, cc.p(this.leftUpPic.x + this.leftUpPic.width + 20, this.leftUpPic.y - this.leftUpPic.height + 20));
    this.leftUpPic.runAction(this.leftUpPic.inAciton);
    this.leftDownPic.inAciton = cc.moveTo(0.2, cc.p(this.leftDownPic.x + this.leftDownPic.width + 20, this.leftDownPic.y + this.leftDownPic.height + 20));
    this.leftDownPic.runAction(this.leftDownPic.inAciton);
    this.rightUpPic.inAciton = cc.moveTo(0.2, cc.p(this.rightUpPic.x - this.rightUpPic.width - 30, this.rightUpPic.y - this.rightUpPic.height - 30));
    this.rightUpPic.runAction(this.rightUpPic.inAciton);
    this.rightDownPic.inAciton = cc.moveTo(0.2, cc.p(this.rightDownPic.x - this.rightDownPic.width - 20, this.rightDownPic.y + this.rightDownPic.height + 80));
    this.rightDownPic.runAction(this.rightDownPic.inAciton);
    var action = cc.sequence(cc.delayTime(0.2), cc.progressTo(0.3, 20), cc.callFunc(function () {
        this.firstPoint.inAction = cc.scaleTo(0.2, 1);
        this.firstPoint.runAction(this.firstPoint.inAction);
        this.firstText.inAction = cc.sequence(cc.delayTime(0.2), cc.spawn(cc.moveTo(0.2, cc.p(this.firstText.x - this.firstText.width / 2, this.firstText.y)), cc.fadeIn(0.5)));
        this.firstText.runAction(this.firstText.inAction);
    }, this), cc.delayTime(0.7), cc.progressTo(0.3, 50), cc.callFunc(function () {
        this.secondPoint.inAction = cc.scaleTo(0.2, 1);
        this.secondPoint.runAction(this.secondPoint.inAction);
        this.secondText.inAction = cc.sequence(cc.delayTime(0.2), cc.spawn(cc.moveTo(0.2, cc.p(this.secondText.x + this.secondText.width / 2, this.secondText.y)), cc.fadeIn(0.5)));
        this.secondText.runAction(this.secondText.inAction);
    }, this), cc.delayTime(0.7), cc.progressTo(0.3, 80), cc.callFunc(function () {
        this.thirdPoint.inAction = cc.scaleTo(0.2, 1);
        this.thirdPoint.runAction(this.thirdPoint.inAction);
        this.thirdText.inAction = cc.sequence(cc.delayTime(0.2), cc.spawn(cc.moveTo(0.2, cc.p(this.thirdText.x - this.thirdText.width / 2, this.thirdText.y)), cc.fadeIn(0.5)));
        this.thirdText.runAction(this.thirdText.inAction);
    }, this), cc.delayTime(0.7), cc.progressTo(0.3, 100), cc.callFunc(function () {
        this.forthPoint.inAction = cc.scaleTo(0.2, 1);
        this.forthPoint.runAction(this.forthPoint.inAction);
        canChangePage = true;
    }, this));
    this.line.runAction(action);
}

上面的代码看似很多,不过,只要分清楚我们的目的,那就很清晰了(ps:initUI只贴出了一部分跟progressTimer相关的代码,具体代码请直接看源码吧)。


我们重点介绍一下cc.progressTimer()

他跟所有Cocos2d-JS的API一样,使用new来创建对象

  • progressTimer的type参数用于设置类型,这里我们使用cc.ProgressTimer.TYPE_BAR也就是条状的progress

  • midPoint参数,用于设置开始progress的起始位置,有点类似于锚点传入一个cc.p()x y范围都是0到1

  • barchangeRate就是每次dt更新的百分比

  • 顺便介绍一下cc.progressTo(duration,percent):主要用于cc.progressTimer当中,从在duration的时间内,

从当前的百分比增加到percent。


介绍了基础的用法之后呢,我们对cc.progressTimer()有了一定的了解,接下来我们主要使用cc.progressTimer里面的cc.progressTo()这个方法去实现第三个场景的动画,经过对图片的测量,我们选定了20%,50%,80%,100%进行分段的价值,所以我们通过cc.sequence(cc.progressTo(),cc.callFunc(),cc.delayTime(),.....)这样一组循环,去分段加载那个图片。

到这里为止,我们比较详细的介绍了Cocos2d-JS一些常用的action,并且用他,实现了一个比较美观的动画,大家有兴趣可以动手亲自去试试。

如社区发表内容存在侵权行为,您可以点击这里查看侵权投诉指引