HTML5 Canvas编写五彩连珠(4):动画
上一節(jié)中,我們留下了一個(gè)flyin的方法沒(méi)有介紹,這里想單獨(dú)寫一篇html5的動(dòng)畫實(shí)現(xiàn)。
在第二節(jié)中我們實(shí)現(xiàn)了畫一個(gè)泡泡,并且成功的擦除了泡泡,但當(dāng)時(shí)也說(shuō)了別把棋盤的線給擦掉了,所以做了偏移量。所以說(shuō)html5 Canvas還是低級(jí), 沒(méi)有圖層的概念,擦掉再想補(bǔ)回來(lái),怎么補(bǔ)? ?答案就是重繪。 ?沒(méi)錯(cuò),整個(gè)Canvas重繪,這樣就能不用擔(dān)心補(bǔ)哪里了。雖然帶來(lái)了性能的損失,但絕對(duì)減少的編碼難度。而且計(jì)算機(jī)的能力也不差這點(diǎn)損失。那么重繪的話,我們?cè)贑anvas是上所有的需要繪制的對(duì)象都應(yīng)該有draw方法。這是必須的。另外,所有的元素都有個(gè)上下的概念,所以要先繪制下面的,再繪制上面的。 而這個(gè)上下就得靠 子元素的概念,這樣在父元素繪制完畢后遍歷其子元素繪制,就不用擔(dān)心掩蓋的問(wèn)題。
如果想把ready區(qū)的3個(gè)泡“飛入”棋盤,就需要讓Canvas在泡移動(dòng)的時(shí)候進(jìn)行重繪,泡泡不動(dòng)時(shí)不需要重繪。泡泡的移動(dòng)很容易實(shí)現(xiàn),只要改變他的x,y坐標(biāo)即可。如果想達(dá)到動(dòng)畫的效果,就得在改變坐標(biāo)期間,定時(shí)重繪,可以使用setInterval來(lái)實(shí)現(xiàn)。
另外,我們不光飛入的動(dòng)作需要重繪,游戲開(kāi)始后玩家還要點(diǎn)擊移動(dòng)一個(gè)泡泡到另外一個(gè)格子,所以這里也要重繪。那繪制的信息這么多,整個(gè)重繪工作都要交給game來(lái)進(jìn)行,game控制所有的父元素。?
game.start就是初始化所有的父元素,
game.over自然不必說(shuō),只是這里沒(méi)有寫具體代碼,結(jié)束時(shí)應(yīng)該無(wú)法繼續(xù)操作泡泡。
game.draw 繪制所有的父元素
game.play 就是重繪方法,需要重繪時(shí)掉用此方法。接收2個(gè)參數(shù),第一個(gè)是重繪時(shí)需要做的動(dòng)作,interval是繪制的間隔時(shí)間。不同的動(dòng)作可能間隔不一樣。
可能這種實(shí)現(xiàn)是野路子,真正的重繪應(yīng)該是游戲開(kāi)始后就不聽(tīng)的調(diào)用重繪方法,而不是具體哪里調(diào)用,只是在具體的精靈(每個(gè)元素)Update自己狀態(tài)就像我這里的action。 ?這里我們暫且這樣實(shí)現(xiàn),后面如果達(dá)不到需求再重構(gòu)這個(gè)重繪的代碼,畢竟核心的代碼不變,只是改改機(jī)制 不是大問(wèn)題。
game.stop 停止重繪,又調(diào)用了一次draw,是為了保證最后的繪制沒(méi)問(wèn)題。
接下來(lái)考慮下flyin飛入的實(shí)現(xiàn)。知道起始和結(jié)束的xy坐標(biāo),飛入的路徑不是問(wèn)題,無(wú)非是x y的加加減減,那么動(dòng)畫方面,我們的game.play的action就是來(lái)加減ready.bubbles的xy坐標(biāo)。飛入的邏輯并非這么簡(jiǎn)單,首先需要3個(gè)沒(méi)染色空格,如果不足3個(gè),那就GameOver了,所以map需要提供一個(gè)返回3個(gè)空格子的方法,另外,飛入之后ready區(qū)要重新生成新的泡泡,而這幾個(gè)被飛的泡泡需要?jiǎng)h除,并且map要對(duì)3個(gè)空格子進(jìn)行染色。 這就完成了整個(gè)飛入效果。
其實(shí)還有一個(gè)邏輯就是 飛入完畢后檢查是否有哪些泡泡可以被消除,這個(gè)我們后面再講。
先看獲取3個(gè)空格的方法:?
獲取3個(gè)隨機(jī)的空格還是挺多代碼的。。。然后就是flyin的實(shí)現(xiàn)了。
首先定一個(gè)一個(gè)status,來(lái)存飛入的狀態(tài)。3個(gè)都飛完畢才能做后面的邏輯。Bubble對(duì)象也為此增加了px和py倆個(gè)成員(即Bubble的實(shí)際坐標(biāo)),這樣才能控制每個(gè)像素的移動(dòng)。 其實(shí)在計(jì)算飛入路徑時(shí)我寫了很久的代碼,別看現(xiàn)在就這么幾行,開(kāi)發(fā)過(guò)程中還是頗費(fèi)力。各種詭異的飛行。。。開(kāi)始是按x++ y++遞增飛行的,這樣就是45°角飛行,但顯然飛行線路(起始到結(jié)束的線)的傾斜度不是45°,那就會(huì)出現(xiàn)先飛完x或y,再走直線,很傻的。所以要用斜率來(lái)計(jì)算當(dāng)前的y坐標(biāo)。而x的坐標(biāo)可以固定常熟移動(dòng)。我畫了一個(gè)斜率的公式,忘記的同學(xué)可以看看下。根據(jù)長(zhǎng)寬的比率,就能計(jì)算當(dāng)前的y值。
?
flyin: function () {var emptys = game.map.getEmptyBubbles();if (emptys.length < 3) {//GAME OVERgame.over();return;}var me = this;var status = [0, 0, 0];game.play(function () {if (status[0] && status[1] && status[2]) {game.stop();status = [0, 0, 0];me.bubbles = [];me.genrate();return;}for (var i = 0; i < me.bubbles.length; i++) {if (status[i]) {continue;}var target = emptys[i];var x2 = target.px + game.map.startX - me.startX;var y2 = target.py + game.map.startY - me.startY;var current = me.bubbles[i];var tmpWidth = 2;if (current.px < x2) {current.py = ((y2 - current.py) / (x2 - current.px)) * tmpWidth + current.py;current.px += tmpWidth;}else if (current.px > x2) {current.py = ((y2 - current.py) / (current.px - x2)) * tmpWidth + current.py;current.px -= tmpWidth;}else {current.py += tmpWidth;}if (current.py > y2) {current.py = y2;}if (current.px > x2) {current.px = x2;}if (current.px == x2 && current.py == y2) {status[i] = 1;current.x = target.x;current.y = target.y;game.map.addBubble(current);console.log(1);}}});}既然我們已經(jīng)實(shí)現(xiàn)了動(dòng)畫效果,那么接下來(lái)順便再實(shí)現(xiàn)一個(gè)動(dòng)畫效果,就是點(diǎn)擊泡泡時(shí),泡泡要做出響應(yīng)(就是忽閃忽閃的),要不然用戶都不知道點(diǎn)了沒(méi)有。所以Bubble也要增加一個(gè)閃動(dòng)的action。
代碼的意思是讓間隔50毫秒,重繪一次光照的亮度,亮度值(外圓的半徑)10和30之間來(lái)回蕩。這樣就亮了暗,暗了再亮。很有意思:)
Bubble.prototype.play = function () {var me = this;var isUp = true;game.play("light_" + this.x + "_" + this.y, function () {if (isUp) {me.light++;}if (!isUp) {me.light--;}if (me.light == 30) {isUp = false;}if (me.light == 10) {isUp = true;}}, 50); };Bubble.prototype.stop = function () {//this.light = 10;var me = this;game.stop("light_" + this.x + "_" + this.y);game.play("restore_" + this.x + "_" + this.y, function () {if (me.light > 10) {me.light--;}else {me.light = 10;game.stop("restore_" + me.x + "_" + me.y);}}, 50); };細(xì)心的朋友可能會(huì)發(fā)現(xiàn),在調(diào)用Game.stop的方法的參數(shù)上增加了一個(gè)參數(shù)。 這里我要說(shuō)明下。如果沒(méi)有參數(shù)的情況,game.play和stop會(huì)造成問(wèn)題,因?yàn)橛玫亩际且粋€(gè)interval,clear的話會(huì)打斷其他的動(dòng)畫,所以我們把每個(gè)action都要傳遞一個(gè)name,這樣就能讓game粒度更細(xì)的控制每個(gè)action了。game的播放代碼也做了響應(yīng)的調(diào)整,如下:
play: function (name, action, interval) {var me = this;this.actions[name] = setInterval(function () {action();me.draw();}, interval || 1);},stop: function (name) {clearInterval(this.actions[name]);this.draw();},?
效果演示地址:http://jsfiddle.net/maddemon/VtMSU/embedded/result/
轉(zhuǎn)載于:https://www.cnblogs.com/mad/archive/2012/03/18/2403404.html
總結(jié)
以上是生活随笔為你收集整理的HTML5 Canvas编写五彩连珠(4):动画的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問(wèn)題。
- 上一篇: Java常见面试题之SQL基础
- 下一篇: 获取用户浏览器类型