日本综合一区二区|亚洲中文天堂综合|日韩欧美自拍一区|男女精品天堂一区|欧美自拍第6页亚洲成人精品一区|亚洲黄色天堂一区二区成人|超碰91偷拍第一页|日韩av夜夜嗨中文字幕|久久蜜综合视频官网|精美人妻一区二区三区

RELATEED CONSULTING
相關(guān)咨詢
選擇下列產(chǎn)品馬上在線溝通
服務(wù)時間:8:30-17:00
你可能遇到了下面的問題
關(guān)閉右側(cè)工具欄

新聞中心

這里有您想知道的互聯(lián)網(wǎng)營銷解決方案
用HTML 5打造斯諾克桌球俱樂部

本文介紹了如何利用HTML5技術(shù)來打造一款非??岬乃怪Z克桌球游戲,文章中詳細(xì)地列出了開發(fā)的全過程,并解說了實現(xiàn)這個游戲的幾個關(guān)鍵點。在文章末尾我向大家提供了游戲的在線實例頁面和源碼下載鏈接,如果你只是想玩玩(需要使用支持HTML5的瀏覽器,建議使用Chrome 12, Internet Explorer 9 或者 Fire Fox 5及其以上版本),那你可以跳過正文拉到頁面最底端去玩玩那個游戲或者下載源碼,但我建議你好好看看實現(xiàn)過程,對我們學(xué)習(xí)HTML5非常有幫助。

目前創(chuàng)新互聯(lián)建站已為近千家的企業(yè)提供了網(wǎng)站建設(shè)、域名、虛擬主機(jī)、網(wǎng)站托管、服務(wù)器托管、企業(yè)網(wǎng)站設(shè)計、汨羅網(wǎng)站維護(hù)等服務(wù),公司將堅持客戶導(dǎo)向、應(yīng)用為本的策略,正道將秉承"和諧、參與、激情"的文化,與客戶和合作伙伴齊心協(xié)力一起成長,共同發(fā)展。

推薦專題:HTML 5 下一代Web開發(fā)標(biāo)準(zhǔn)詳解

毫無疑問,我們已經(jīng)目睹了HTML5背后的那場偉大的Web開發(fā)革命。經(jīng)過那么多年HTML4的統(tǒng)治,一場全新的運動即將完全改變現(xiàn)在的Web世界。正是他釋放出來的現(xiàn)代化氣息和豐富的用戶體驗,讓它很快地成為了一個獨特的插件運行在類似Flash和Silverlight的框架之上。

如果你是一個非常年輕的開發(fā)者,也許你是剛剛在開始學(xué)習(xí)HTML5,所以可能你并沒有注意到他有太大的變化。在任何時候,我希望這篇文章能夠幫助到你,當(dāng)然,也希望像我一樣的老手能從中學(xué)到一些新的花樣。

你的點評對我來說非常重要,所以我很期待你的來信。當(dāng)然能讓我更興奮的是當(dāng)你在那個游戲畫面上右擊時暗暗地說一句“Hey,這居然不是Flash!也不是Silverlight!”

系統(tǒng)要求

想要使用本文提供的HTML5桌球應(yīng)用,你必須安裝下面的這些瀏覽器:Chrome 12, Internet Explorer 9 or Fire Fox 5

游戲規(guī)則

也許你已經(jīng)知道這是一個什么樣的游戲了,是的,這是“英式斯諾克”,實際上更確切的說是“簡易版英式斯諾克”,因為沒有實現(xiàn)所有的斯諾克游戲規(guī)則。你的目標(biāo)是按順序?qū)⒛繕?biāo)球灌入袋中,從而比其他選手得到更多的分?jǐn)?shù)。輪到你的時候,你就要出桿了:根據(jù)提示,你必須先打進(jìn)一個紅色球得到1分,如果打進(jìn)了,你就可以繼續(xù)打其他的球 - 但是這次你只能打彩色球了(也就是除紅色球以外的球)。如果成功打進(jìn),你將會得到各自彩球?qū)?yīng)的分?jǐn)?shù)。然后被打進(jìn)的彩球會回到球桌上,你可以繼續(xù)擊打其他的紅球。這樣周而復(fù)始,直到你失敗為止。當(dāng)你把所有的紅球都打完以后,球桌上就只剩下6個彩球了,你的目標(biāo)是將這6個彩球按以下順序依次打入袋中:黃(2分)、綠(3分)、棕(4分)、藍(lán)(5分)、粉(6分)、黑(7分)。如果一個球不是按上面順序打進(jìn)的,那它將會回到球桌上,否則,它最終會留在袋里。當(dāng)所有球都打完后,游戲結(jié)束,得分最多的人勝出。

犯規(guī)處理

為了處罰你的犯規(guī),其他選手將會得到你的罰分:

◆ 白球掉入袋中罰4分

◆ 白球第一次擊中的球是錯誤的話罰第一個球的分值

◆ 第一個錯誤的球掉入袋中罰第一個球的分值

◆ 處罰的分?jǐn)?shù)至少是4

下面的這段代碼展示了我是如何來計算犯規(guī)的:

 
 
 
 
  1. var strokenBallsCount = 0;  
  2. console.log('strokenBalls.length: ' + strokenBalls.length);  
  3.     for (var i = 0; i < strokenBalls.length; i++) {  
  4.         var ball = strokenBalls[i];  
  5.         //causing the cue ball to first hit a ball other than the ball on  
  6.         if (strokenBallsCount == 0) {  
  7.             if (ball.Points != teams[playingTeamID - 1].BallOn.Points) {  
  8.                 if (ball.Points == 1 || teams[playingTeamID - 1].BallOn.Points == 1 ||   
  9.                 fallenRedCount == redCount) {  
  10.                     if (teams[playingTeamID - 1].BallOn.Points < 4) {  
  11.                         teams[playingTeamID - 1].FoulList[teams[playingTeamID - 1]  
  12.                         .FoulList.length] = 4;  
  13.                         $('#gameEvents').append('  
  14. Foul 4 points :  Expected ' +  
  15.                          teams[playingTeamID - 1].BallOn.Points + ', but hit ' + ball.Points);  
  16.                     }  
  17.                     else {  
  18.                         teams[playingTeamID - 1].FoulList[teams[playingTeamID - 1]  
  19.                         .FoulList.length] = teams[playingTeamID - 1].BallOn.Points;  
  20.                         $('#gameEvents').append('  
  21. Foul ' + teams[playingTeamID - 1]  
  22.                         .BallOn.Points + ' points :  Expected ' + teams[playingTeamID - 1]  
  23.                         .BallOn.Points + ', but hit ' + ball.Points);  
  24.                     }  
  25.                     break;  
  26.                 }  
  27.             }  
  28.         }  
  29.    
  30.         strokenBallsCount++;  
  31.     }  
  32.    
  33.     //Foul: causing the cue ball to miss all object balls  
  34.     if (strokenBallsCount == 0) {  
  35.         teams[playingTeamID - 1].FoulList[teams[playingTeamID - 1].FoulList.length] = 4;  
  36.         $('#gameEvents').append('  
  37. Foul 4 points :  causing the cue ball   
  38.         to miss all object balls');  
  39.     }  
  40.    
  41.     for (var i = 0; i < pottedBalls.length; i++) {  
  42.         var ball = pottedBalls[i];  
  43.         //causing the cue ball to enter a pocket  
  44.         if (ball.Points == 0) {  
  45.             teams[playingTeamID - 1].FoulList[teams[playingTeamID - 1].FoulList.length] = 4;  
  46.             $('#gameEvents').append('  
  47. Foul 4 points :  causing the cue ball  
  48.              to enter a pocket');  
  49.         }  
  50.         else {  
  51.             //causing a ball different than the target ball to enter a pocket  
  52.             if (ball.Points != teams[playingTeamID - 1].BallOn.Points) {  
  53.                 if (ball.Points == 1 || teams[playingTeamID - 1].BallOn.Points == 1  
  54.                  || fallenRedCount == redCount) {  
  55.                     if (teams[playingTeamID - 1].BallOn.Points < 4) {  
  56.                         teams[playingTeamID - 1].FoulList[teams[playingTeamID - 1]  
  57.                         .FoulList.length] = 4;  
  58.                         $('#gameEvents').append('  
  59. Foul 4 points : '  
  60.                          + ball.Points + ' was potted, while ' + teams[playingTeamID - 1]  
  61.                          .BallOn.Points + ' was expected');  
  62.                         $('#gameEvents').append('  
  63. ball.Points: ' + ball.Points);  
  64.                         $('#gameEvents').append('  
  65. teams[playingTeamID - 1]  
  66.                         .BallOn.Points: ' + teams[playingTeamID - 1].BallOn.Points);  
  67.                         $('#gameEvents').append('  
  68. fallenRedCount: ' + fallenRedCount);  
  69.                         $('#gameEvents').append('  
  70. redCount: ' + redCount);  
  71.                     }  
  72.                     else {  
  73.                         teams[playingTeamID - 1].FoulList[teams[playingTeamID - 1]  
  74.                         .FoulList.length] = teams[playingTeamID - 1].BallOn.Points;  
  75.                         $('#gameEvents').append('  
  76. Foul ' + teams[playingTeamID - 1]  
  77.                         .BallOn.Points + ' points : ' + ball.Points + ' was potted, while '  
  78.                          + teams[playingTeamID - 1].BallOn.Points + ' was expected');  
  79.                     }  
  80.                 }  
  81.             }  
  82.         }  
  83.     } 

得分

我們根據(jù)下面的規(guī)則來計算得分:紅(1分)、黃(2分)、綠(3分)、棕(4分)、藍(lán)(5分)、粉(6分)、黑(7分)。代碼如下:

 
 
 
 
  1. if (teams[playingTeamID - 1].FoulList.length == 0) {  
  2.            for (var i = 0; i < pottedBalls.length; i++) {  
  3.                var ball = pottedBalls[i];  
  4.                //legally potting reds or colors  
  5.                wonPoints += ball.Points;  
  6.                $('#gameEvents').append('  
  7. Potted +' + ball.Points + ' points.');  
  8.            }  
  9.        }  
  10.        else {  
  11.            teams[playingTeamID - 1].FoulList.sort();  
  12.            lostPoints = teams[playingTeamID - 1].FoulList[teams[playingTeamID - 1].FoulList.length - 1];  
  13.            $('#gameEvents').append('  
  14. Lost ' + lostPoints + ' points.');  
  15.        }  
  16.        teams[playingTeamID - 1].Points += wonPoints;  
  17.        teams[awaitingTeamID - 1].Points += lostPoints; 

選手的閃動動畫頭像

游戲是有兩位選手參與的,每一位選手都有自己的昵稱和頭像,選手的昵稱我們就簡單地以“player 1”和“player 2”來命名了(也許讓用戶自己輸入會更漂亮)。每位選手的頭像是一只正在打桌球的可愛小狗。當(dāng)輪到其中一位選手時,他的頭像就會有一閃一閃的動畫效果,同時對手的頭像會停止閃動。

這個效果我們是通過改變img元素的CSS3屬性opacity的值來實現(xiàn)的:我們使用jquery的animatio函數(shù)讓opacity的值在0-1.0之間變化。

 
 
 
 
  1. function animateCurrentPlayerImage() {  
  2.     var otherPlayerImageId = 0;  
  3.     if (playingTeamID == 1)  
  4.         otherPlayerImageId = 'player2Image';  
  5.     else  
  6.         otherPlayerImageId = 'player1Image';  
  7.     var playerImageId = 'player' + playingTeamID + 'Image';  
  8.     $('#' + playerImageId).animate({  
  9.         opacity: 1.0  
  10.     }, 500, function () {  
  11.         $('#' + playerImageId).animate({  
  12.             opacity: 0.0  
  13.         }, 500, function () {  
  14.             $('#' + playerImageId).animate({  
  15.                 opacity: 1.0  
  16.             }, 500, function () {  
  17.             });  
  18.         });  
  19.     });  
  20.    
  21.     $('#' + otherPlayerImageId).animate({  
  22.         opacity: 0.25  
  23.     }, 1500, function () {  
  24.     });  
  25. }  

力量控制條

一個優(yōu)秀的斯諾克選手都能很好地把握住每一桿的力度.不同的技巧需要不同的擊球方式:直接的,間接的,或者利用邊角的等等。不同方向和不同力度的組合可以構(gòu)造成千上萬種可能的路徑。幸運的是,這個游戲提供了一個非常漂亮的力度控制條,可以幫助選手在擊球前調(diào)整他們的球桿。

為了達(dá)到這一點,我們使用了HTML5的meter元素標(biāo)簽,它可以完成測量距離的工作。meter標(biāo)簽最好在知道這次測量的最小值和最大值的情況下使用。在我們的這個例子中,這個值在0到100之間,因為IE9不支持meter,所以我用了一張背景圖來替代,這樣效果也是一樣的。

 
 
 
 
  1. #strengthBar { position: absolute; margin:375px 0 0 139px;   
  2.     width: 150px; color: lime; background-color: orange;   
  3.     z-index: 5;} 

當(dāng)你點擊了力度條后,你實際上是選擇了一個新的力度。一開始你可能不是很熟練,但在真實世界中,這是需要時間來訓(xùn)練自己的能力的。點擊力度條的代碼如下:

 
 
 
 
  1. $('#strengthBar').click(function (e) {  
  2.     var left = $('#strengthBar').css('margin-left').replace('px', '');  
  3.     var x = e.pageX - left;  
  4.     strength = (x / 150.0);  
  5.     $('#strengthBar').val(strength * 100);  
  6. });  

在當(dāng)前選手的頭像框里面,你會注意到有一個小球,我叫他“ball on”,就是當(dāng)前選手在規(guī)定時間內(nèi)應(yīng)該要擊打的那個球。如果這個球消失了,那選手將失去4分。同樣如果選手第一次擊中的球不是框內(nèi)顯示的球,那他也將失去4分。

這個“ball on”是直接將canvas元素覆蓋在用戶頭像上的,所以你在頭像上看到的那個球,他看起來像是在標(biāo)準(zhǔn)的div上蓋了一個img元素,但是這個球并不是img實現(xiàn)的。當(dāng)然我們也不能直接在div上畫圓弧和直線,這就是為什么我要將canvas覆蓋到頭像上的原因了。看看代碼吧:

 
 
 
 
  1.    
  2.         
  3.  
 
 
 
 
  1. var player1BallOnContext = player1BallOnCanvas.getContext('2d');  
  2. var player2BallOnContext = player2BallOnCanvas.getContext('2d');  
  3. .  
  4. .  
  5. .  
  6. function renderBallOn() {  
  7.     player1BallOnContext.clearRect(0, 0, 500, 500);  
  8.     player2BallOnContext.clearRect(0, 0, 500, 500);  
  9.     if (playingTeamID == 1) {  
  10.         if (teams[0].BallOn != null)  
  11.             drawBall(player1BallOnContext, teams[0].BallOn, new Vector2D(30, 120), 20);  
  12.     }  
  13.     else {  
  14.         if (teams[1].BallOn != null)  
  15.             drawBall(player2BallOnContext, teams[1].BallOn, new Vector2D(30, 120), 20);  
  16.         player1BallOnContext.clearRect(0, 0, 133, 70);  
  17.     }  

旋轉(zhuǎn)屋頂上的電風(fēng)扇

在這個游戲中這把電風(fēng)扇純屬拿來玩玩有趣一把的。那為什么這里要放一把電風(fēng)扇?是這樣的,這個游戲的名字叫HTML5斯諾克俱樂部,放一把電風(fēng)扇就有俱樂部的氣氛了,當(dāng)然,我也是為了說明如何實現(xiàn)CSS3的旋轉(zhuǎn)。

實現(xiàn)這個非常簡單:首先我們需要一張PNG格式的電扇圖片。只是我們并沒有用電扇本身的圖片,我們用他的投影。通過顯示風(fēng)扇在球桌上的投影,讓我們覺得它在屋頂上旋轉(zhuǎn),這樣就達(dá)到了我們目的:

 
 
 
 
  1. #roofFan { position:absolute; left: 600px; top: -100px; width: 500px; height: 500px;   
  2.     border: 2px solid transparent; background-image: url('/Content/Images/roofFan.png');   
  3.     background-size: 100%; opacity: 0.3; z-index: 2;}  
  4. .  
  5. .  
  6. .  
  7.  
 

為了獲得更為逼真的氣氛,我用Paint.Net軟件將電扇圖片平滑化了,現(xiàn)在你再也看不到電扇的邊緣了。我覺得這是達(dá)到如此酷的效果最為簡單的辦法。

除了用了這圖像處理的把戲,我們僅僅使用了一個帶背景圖的普通的div元素,這并沒有什么特別。既然我們已經(jīng)得到了電扇圖片,我們就要讓它開始旋轉(zhuǎn)了。這里我們使用CSS3的rotate屬性來實現(xiàn)這一切。

球桿動畫

球桿的動畫對于這個游戲也不是必需的,但是這的確為此添加了不少樂趣。當(dāng)你開始用鼠標(biāo)在球桌上移動時,你會注意到球桿的確是跟著你的鼠標(biāo)在轉(zhuǎn)動。這就是說球桿會一直保持跟隨鼠標(biāo)的移動,就像你身臨其境一般真實。因為選手只能用他的眼睛來瞄準(zhǔn),所以這個效果也會對選手有所幫助。

球桿是單獨一張PNG圖片,圖片本身不直接以img的形式展現(xiàn),也不以背景的形式展現(xiàn),相反,它是直接展現(xiàn)在一個專門的canvas上的。當(dāng)然我們也可以用div和css3來達(dá)到同樣的效果,但我覺得這樣能更好的說明如何在canvas上展現(xiàn)圖片。

首先,canvas元素會占據(jù)幾乎整個頁面的寬度。請注意這個特別的canvas有一個很大的z-index值,這樣球桿就可以一直在每個球的上方而不會被球遮蓋。當(dāng)你在球桌上移動鼠標(biāo)時,目標(biāo)點會實時更新,這時候球桿圖片會進(jìn)行2次轉(zhuǎn)換:首先,通過計算得到母球的位置,其次翻轉(zhuǎn)母球周圍的球桿,通過這2步我們就得到了鼠標(biāo)所在點和母球的中心點。

 
 
 
 
  1. #cue { position:absolute; }  
  2. .  
  3. .  
  4. .  
  5. if (drawingtopCanvas.getContext) {  
  6.     var cueContext = drawingtopCanvas.getContext('2d');  
  7. }  
  8. .  
  9. .  
  10. .  
  11. var cueCenter = [15, -4];  
  12. var cue = new Image;  
  13. cue.src = '<%: Url.Content("../Content/Images/cue.PNG") %>';  
  14.    
  15. var shadowCue = new Image;  
  16. shadowCue.src = '<%: Url.Content("../Content/Images/shadowCue.PNG") %>';  
  17. cueContext.clearRect(0, 0, topCanvasWidth, topCanvasHeight);  
  18.    
  19.     if (isReady) {  
  20.         cueContext.save();  
  21.         cueContext.translate(cueBall.position.x + 351, cueBall.position.y + 145);  
  22.         cueContext.rotate(shadowRotationAngle - Math.PI / 2);  
  23.         cueContext.drawImage(shadowCue, cueCenter[0] + cueDistance, cueCenter[1]);  
  24.         cueContext.restore();  
  25.         cueContext.save();  
  26.         cueContext.translate(cueBall.position.x + 351, cueBall.position.y + 140);  
  27.         cueContext.rotate(angle - Math.PI / 2);  
  28.         cueContext.drawImage(cue, cueCenter[0] + cueDistance, cueCenter[1]);  
  29.         cueContext.restore();  
  30.     } 

為了讓球桿變得更真實我們?yōu)榍驐U添加了投影,并且我們故意讓球桿投影的旋轉(zhuǎn)角度和球桿的角度不一樣,我們這樣做是為了讓球桿有3D的效果。最終的效果實在是太酷了。

#p#

推拉球桿

這個球桿動畫模仿了真實人類的特征:你是否看到過斯諾克選手在瞄準(zhǔn)的時候會推拉球桿?我們通過HTML5改變母球和球桿的距離實現(xiàn)了這一效果。當(dāng)達(dá)到一個極點是球桿會被拉回來,然后到達(dá)另一個極點時又會被向前推。這樣周而復(fù)始,知道選手停止移動鼠標(biāo)。

 
 
 
 
  1. var cueDistance = 0;  
  2. var cuePulling = true;  
  3. .  
  4. .  
  5. .  
  6.         function render() {  
  7.             .  
  8.             .  
  9.             .  
  10.    
  11.             if (cuePulling) {  
  12.                 if (lastMouseX == mouseX ||  
  13.                 lastMouseY == mouseY) {  
  14.                     cueDistance += 1;  
  15.                 }  
  16.                 else {  
  17.                     cuePulling = false;  
  18.                     getMouseXY();  
  19.                 }  
  20.             }  
  21.             else {  
  22.    
  23.                 cueDistance -= 1;  
  24.             }  
  25.    
  26.             if (cueDistance > 40) {  
  27.                 cueDistance = 40;  
  28.                 cuePulling = false;  
  29.             }  
  30.             else if (cueDistance < 0) {  
  31.                 cueDistance = 0;  
  32.                 cuePulling = true;  
  33.             }  
  34.             .  
  35.             .  
  36.             . 

顯示目標(biāo)路徑

當(dāng)選手移動鼠標(biāo)時,我們會在母球和當(dāng)前鼠標(biāo)點之間畫一條虛線。這對選手們長距離瞄準(zhǔn)相當(dāng)?shù)谋憷?/p>

這條目標(biāo)路徑只有在等待用戶擊球時才會顯示:

 
 
 
 
  1. if (!cueBall.pocketIndex) {  
  2.     context.strokeStyle = '#888';  
  3.     context.lineWidth = 4;  
  4.     context.lineCap = 'round';  
  5.     context.beginPath();  
  6.    
  7.     //here we draw the line  
  8.     context.dashedLine(cueBall.position.x, cueBall.position.y, targetX, targetY);  
  9.    
  10.     context.closePath();  
  11.     context.stroke();  

需要注意的是在HTML5 canvas中并沒有內(nèi)置函數(shù)來畫虛線。幸運的是有一個叫phrogz的家伙在StackOverflow網(wǎng)站上發(fā)布了一個關(guān)于這個畫虛線的帖子:

 
 
 
 
  1. //function kindly provided by phrogz at:  
  2. //http://stackoverflow.com/questions/4576724/dotted-stroke-in-canvas  
  3. var CP = window.CanvasRenderingContext2D && CanvasRenderingContext2D.prototype;  
  4. if (CP && CP.lineTo) {  
  5.     CP.dashedLine = function (x, y, x2, y2, dashArray) {  
  6.         if (!dashArray) dashArray = [10, 5];  
  7.         var dashCount = dashArray.length;  
  8.         this.moveTo(x, y);  
  9.         var dx = (x2 - x), dy = (y2 - y);  
  10.         var slope = dy / dx;  
  11.         var distRemaining = Math.sqrt(dx * dx + dy * dy);  
  12.         var dashIndex = 0, draw = true;  
  13.         while (distRemaining >= 0.1) {  
  14.             var dashLength = dashArray[dashIndex++ % dashCount];  
  15.             if (dashLength > distRemaining) dashLength = distRemaining;  
  16.             var xStep = Math.sqrt(dashLength * dashLength / (1 + slope * slope));  
  17.    
  18.             var signal = (x2 > x ? 1 : -1);  
  19.    
  20.             x += xStep * signal;  
  21.             y += slope * xStep * signal;  
  22.             this[draw ? 'lineTo' : 'moveTo'](x, y);  
  23.             distRemaining -= dashLength;  
  24.             draw = !draw;  
  25.         }  
  26.     }  

顯示跟蹤路徑

當(dāng)選手擊打母球后,母球會在球桌上留下一條跟蹤線,用來標(biāo)明其上一個點的位置。

創(chuàng)建這個跟蹤路徑比前面提到的目標(biāo)路徑復(fù)雜一點。首先我必須去實例化一個Queue對象,這個項目中的Queue對象原型由Stephen Morley提供。

 
 
 
 
  1. var tracingQueue = new Queue(); 

一旦球開始運動,我們就將母球的實時位置壓入這個Queue中:

 
 
 
 
  1. if (renderStep % 2 == 0) {  
  2.     draw();  
  3.     enqueuePosition(new Vector2D(cueBall.position.x, cueBall.position.y));  

enqueuePosition函數(shù)確保了我們只保存前20個點的位置,這也就是為什么我們只讓顯示最近的母球的運動路徑的原因。

 
 
 
 
  1. function enqueuePosition(position) {  
  2.     tracingQueue.enqueue(position);  
  3.     var len = tracingQueue.getLength();  
  4.    
  5.     if (len > 20) {  
  6.         tracingQueue.dequeue();  
  7.     }  

接下來,我們要遍歷Queue中的數(shù)據(jù),從而來創(chuàng)建這條跟蹤路徑:

 
 
 
 
  1. //drawing the tracing line  
  2. var lastPosX = cueBall.position.x;  
  3. var lastPosY = cueBall.position.y;  
  4.    
  5. var arr = tracingQueue.getArray();  
  6.    
  7. if (!cueBall.pocketIndex) {  
  8.     context.strokeStyle = '#363';  
  9.     context.lineWidth = 8;  
  10.     context.lineCap = 'round';  
  11.    
  12.     context.beginPath();  
  13.     var i = arr.length;  
  14.     while (--i > -1) {  
  15.         var posX = arr[i].x;  
  16.         var posY = arr[i].y;  
  17.         context.dashedLine(lastPosX, lastPosY, posX, posY, [10,200,10,20]);  
  18.         lastPosX = posX;  
  19.         lastPosY = posY;  
  20.     }  
  21.    
  22.     context.closePath();  
  23.     context.stroke();  

繪制小球

小球和他們的投影都是呈現(xiàn)在一個特殊的canvas上(在球桿canvas下方)。

在呈現(xiàn)小球時,我們先要呈現(xiàn)其投影,這樣做主要是為了模擬3D的環(huán)境。每一個小球必須有投影,我們對每個小球的投影位置都會有一點細(xì)微的不同,這些細(xì)微差別表明了小球是在不同方向被投射的,也說明了光源所在的位置。

每個小球是由一個公共函數(shù)來畫的,函數(shù)有兩個參數(shù):1)canvas context;2)小球?qū)ο蟆:瘮?shù)先畫出一個完整的圓弧然后根據(jù)小球?qū)ο筇峁┑念伾珜⑦@個圓弧線性填充。

每一個小球?qū)ο笥?中顏色:光亮色、中色和暗色,這些顏色就是用來創(chuàng)建線性漸變顏色的,3D效果也是這樣做出來的。

 
 
 
 
  1. function drawBall(context, ball, newPosition, newSize) {  
  2.     var position = ball.position;  
  3.     var size = ball.size;  
  4.    
  5.     if (newPosition != null)  
  6.         position = newPosition;  
  7.    
  8.     if (newSize != null)  
  9.         size = newSize;  
  10.    
  11.     //main circle  
  12.     context.beginPath();  
  13.     context.fillStyle = ball.color;  
  14.     context.arc(position.x, position.y, size, 0, Math.PI * 2, true);  
  15.    
  16.     var gradient = context.createRadialGradient(  
  17.         position.x - size / 2, position.y - size / 2, 0, position.x,  
  18.         position.y, size );  
  19.    
  20.     //bright spot  
  21.     gradient.addColorStop(0, ball.color);  
  22.     gradient.addColorStop(1, ball.darkColor);  
  23.     context.fillStyle = gradient;  
  24.     context.fill();  
  25.     context.closePath();  
  26.    
  27.     context.beginPath();  
  28.     context.arc(position.x, position.y, size * 0.85, (Math.PI / 180) * 270,   
  29.     (Math.PI / 180) * 200, true);  
  30.     context.lineTo(ball.x, ball.y);  
  31.     var gradient = context.createRadialGradient(  
  32.         position.x - size * .5, position.y - size * .5,  
  33.         0, position.x, position.y, size);  
  34.    
  35.     gradient.addColorStop(0, ball.lightColor);  
  36.     gradient.addColorStop(0.5, 'transparent');  
  37.     context.fillStyle = gradient;  
  38.     context.fill();  
  39. }  
  40.    
  41. function drawBallShadow(context, ball) {  
  42.     //main circle  
  43.     context.beginPath();  
  44.     context.arc(ball.position.x + ball.size * .25, ball.position.y + ball.size * .25,  
  45.      ball.size * 2, 0, Math.PI * 2, true);  
  46.    
  47.     try {  
  48.         var gradient = context.createRadialGradient(  
  49.             ball.position.x + ball.size * .25, ball.position.y + ball.size * .25,  
  50.             0, ball.position.x + ball.size * .25, ball.position.y + ball.size * .25,  
  51.             ball.size * 1.5 );  
  52.     }  
  53.     catch (err) {  
  54.         alert(err);  
  55.         alert(ball.position.x + ',' + ball.position.y);  
  56.     }  
  57.    
  58.     gradient.addColorStop(0, '#000000');  
  59.     gradient.addColorStop(1, 'transparent');  
  60.     context.fillStyle = gradient;  
  61.     context.fill();  
  62.     context.closePath();  

檢測小球之間的碰撞

小球以快速和連續(xù)的方式呈現(xiàn)在canvas上:首先,我們清空canvas,然后在上面繪制投影,再繪制小球,最后更新小球的位置坐標(biāo),這樣周而復(fù)始。在這個期間,我們需要檢查小球是否與另一個小球發(fā)生了碰撞,我們通過對小球的碰撞檢測來完成這些的。

 
 
 
 
  1. function isColliding(ball1, ball2) {  
  2.     if (ball1.pocketIndex == null && ball2.pocketIndex == null) {  
  3.         var xd = (ball1.position.x - ball2.position.x);  
  4.         var yd = (ball1.position.y - ball2.position.y);  
  5.    
  6.         var sumRadius = ball1.size + ball2.size;  
  7.         var sqrRadius = sumRadius * sumRadius;  
  8.    
  9.         var distSqr = (xd * xd) + (yd * yd);  
  10.    
  11.         if (Math.round(distSqr) <= Math.round(sqrRadius)) {  
  12.    
  13.             if (ball1.Points == 0) {  
  14.                 strokenBalls[strokenBalls.length] = ball2;  
  15.             }  
  16.             else if (ball2.Points == 0) {  
  17.                 strokenBalls[strokenBalls.length] = ball1;  
  18.             }  
  19.             return true;  
  20.         }  
  21.     }  
  22.     return false;  

文章名稱:用HTML 5打造斯諾克桌球俱樂部
網(wǎng)站URL:http://www.dlmjj.cn/article/cocppce.html