成人免费xxxxx在线视频软件_久久精品久久久_亚洲国产精品久久久_天天色天天色_亚洲人成一区_欧美一级欧美三级在线观看

用Horizon搭建可擴展的Javascript移動應用后端方案

譯文
移動開發 Android
Horizon是一個著名的跨平臺可擴展的后端框架,適用于構建跨平臺基于JavaScript的移動應用程序,尤其是那些需要實時功能的應用。這個框架是由來自RethinkDB產品的程序員開發的,因此使用RethinkDB作為默認數據庫。

【51CTO.com快譯】

簡介

Horizon是一個著名的跨平臺可擴展的后端框架,適用于構建跨平臺基于JavaScript的移動應用程序,尤其是那些需要實時功能的應用。這個框架是由來自RethinkDB產品的程序員開發的,因此使用RethinkDB作為默認數據庫。如果你還不熟悉RethinkDB,那么你只需知識它是一個開放源碼的支持實時功能的數據庫(https://www.rethinkdb.com)。

Horizon框架公開一組客戶端API來允許你與底層數據庫進行交互。這意味著,你不必編寫任何后端代碼。你要做的就是,搭建一個新的服務器,運行它,Horizon將會自動管理其他內容。借助于Horizon,你可以輕松地實現實時連接的客戶端和服務器之間的數據同步。

如果你想要了解更多關于Horizon的消息,請查閱其 faq頁面(http://horizon.io/faq/)。

在本教程中,你要使用Icon和Horizon來協同開發一個Tic-Tac-Toe井字游戲。因此,閱讀本文的前提是假定你已經了解Icon和Horizon,所以我不打算解釋程序中Icon相關的特定代碼。當然,如果你想要一點有關Icon的背景知識的話,我建議你去查閱這個網址http://ionicframework.com/getting-started/。如果你想繼續閱讀本文內容,那么請你先下載文章的示例工程源碼(https://github.com/anchetaWern/ionic-horizon-tictactoe)。

下圖給出的是本文示例應用程序最終的結果快照。

 

安裝Horizon

RethinkDB用作Horizon的數據庫。因此,在安裝Horizon之前你需要先安裝RethinkDB。有關安裝RethinkDB的具體信息,你可以從網址https://www.rethinkdb.com/docs/install/處找到答案。

一旦安裝了RethinkDB,你就可以在終端程序中執行以下命令通過npm工具來安裝Horizon:

npm install -g horizon

Horizon服務器開發

Horizon服務器用作應用程序的后端。每當應用程序執行代碼時,它要與數據庫進行通信。

您可以通過在您的終端執行以下命令來創建一個新的Horizon服務器:

hz init tictactoe-server

這個命令將創建RethinkDB數據庫并提供Horizon所使用的文件。

一旦創建了服務器,您可以通過執行以下命令運行它:

hz serve --dev

在上面的命令中,指定-dev作為一個選項。這意味著,你想要運行一個開發服務器。在開發服務器中會設置以下選項:

--secure no:這意味著websocket和文件不會通過加密連接提供服務。

--permissions no:禁用權限約束。這意味著,任何客戶端都可以在數據庫中執行任何他們想執行的操作。Horizon的權限系統基于白名單。這意味著,默認情況下,所有用戶都沒有權限來做任何事情。你必須顯式地指定允許哪些操作。

--auto-create-collection yes:在首次使用時自動創建一個集合。在Horizon中,集合相當于關系數據庫中的表。此選項設置為true意味著,每次客戶端使用一個新的集合,它都會被自動創建。

--auto-create-index yes:在首次使用中自動創建索引。

--start-rethinkdb yes:在當前目錄中自動啟動RethinkDB的一個新實例。

--allow-unauthenticated yes:允許未經身份驗證的用戶來執行數據庫操作。

--allow-anonymous yes:允許匿名用戶執行數據庫操作。

--serve-static ./dist:啟用靜態文件服務。如果你想要在瀏覽器中測試與Horizon API的交互時,這是很有用的。Horizon服務器默認運行在端口8181,所以你可以通過訪問地址http://localhost:8181來訪問服務器。

【注意】--dev選項永遠不要用于生產環境下,因為它會打開大量的易于被攻擊者能夠利用的漏洞。

構建Ionic應用程序

現在,我們已經作好了充分準備。接下來,我們著手創建一個Ionic程序框架,命令如下:

ionic start tictactoe blank

安裝Chance.js

接下來,您需要安裝chance.js,這是一個JavaScript實用程序庫,用于生成隨機數據。在本應用程序中,我們使用它來為玩家生成一個唯一的ID。你可以通過bower工具并使用下面的命令來安裝chance.js:

bower install chance

創建index.html

現在,打開文件www/index.html,并把其內容修改為如下:

  1. <!DOCTYPE html> 
  2.  
  3. <html> 
  4.  
  5. <head> 
  6.  
  7. <meta charset="utf-8"
  8.  
  9. <meta name="viewport" content="initial-scale=1, maximum-scale=1, user-scalable=no, width=device-width"
  10.  
  11. <title></title> 
  12.  
  13. <link href="lib/ionic/css/ionic.css" rel="stylesheet"
  14.  
  15. <link href="css/style.css" rel="stylesheet"
  16.  
  17. <!-- IF using Sass (run gulp sass first), then uncomment below and remove the CSS includes above 
  18.  
  19. <link href="css/ionic.app.css" rel="stylesheet"
  20.  
  21. --> 
  22.  
  23. <!-- chance.js --> 
  24.  
  25. <script src="lib/chance/dist/chance.min.js"></script> 
  26.  
  27. <!-- ionic/angularjs js --> 
  28.  
  29. <script src="lib/ionic/js/ionic.bundle.js"></script> 
  30.  
  31. <!-- cordova script (this will be a 404 during development) --> 
  32.  
  33. <script src="cordova.js"></script> 
  34.  
  35. <!-- horizon script --> 
  36.  
  37. <script src="http://127.0.0.1:8181/horizon/horizon.js"></script> 
  38.  
  39. <!-- your app's js --> 
  40.  
  41. <script src="js/app.js"></script> 
  42.  
  43. <!--main app logic --> 
  44.  
  45. <script src="js/controllers/HomeController.js"></script> 
  46.  
  47. </head> 
  48.  
  49. <body ng-app="starter"
  50.  
  51. <ion-nav-view></ion-nav-view
  52.  
  53. </body> 
  54.  
  55. </html> 

上面的代碼大部分來自于Icon空白向導模板生成的樣板代碼。現在,我們來添加對chance.js腳本的引用:

  1. <script src="lib/chance/dist/chance.min.js"></script> 

Horizon服務器將自動提供Horizon腳本服務,代碼如下:

  1. <script src="http://127.0.0.1:8181/horizon/horizon.js"></script> 

【注意】如果你以后想部署這些內容的話,你必須修改URL。

接下來,主應用程序邏輯位于下面這個腳本文件中:

  1. <script src="js/controllers/HomeController.js"></script> 

編寫主程序app.js

文件app.js是運行初始化應用程序代碼的地方。下面,需要打開文件www/js/app.js并把如下內容添加到run函數的下面:

  1. .config(function($stateProvider, $urlRouterProvider) { 
  2.  
  3. $stateProvider 
  4.  
  5. .state('home', { 
  6.  
  7. cache: false
  8.  
  9. url: '/home'
  10.  
  11. templateUrl: 'templates/home.html' 
  12.  
  13. }); 
  14.  
  15. // if none of the above states are matched, use this as the fallback 
  16.  
  17. $urlRouterProvider.otherwise('/home'); 
  18.  
  19. }); 

這將為默認的應用程序頁設置一個路由。此路由將指定頁面所使用的模板和可以訪問它的URL。

開發控制器程序HomeController.Js

現在,我們在路徑www/js/controllers下創建一個控制器文件HomeController.js,并修改其代碼如下:

  1. (function(){ 
  2.  
  3. angular.module('starter'
  4.  
  5. .controller('HomeController', ['$scope', HomeController]); 
  6.  
  7. function HomeController($scope){ 
  8.  
  9. var me = this; 
  10.  
  11. $scope.has_joined = false
  12.  
  13. $scope.ready = false
  14.  
  15. const horizon = Horizon({host: 'localhost:8181'}); 
  16.  
  17. horizon.onReady(function(){ 
  18.  
  19. $scope.$apply(function(){ 
  20.  
  21. $scope.ready = true
  22.  
  23. }); 
  24.  
  25. }); 
  26.  
  27. horizon.connect(); 
  28.  
  29. $scope.join = function(username, room){ 
  30.  
  31. me.room = horizon('tictactoe'); 
  32.  
  33. var id = chance.integer({min: 10000, max: 999999}); 
  34.  
  35. me.id = id; 
  36.  
  37. $scope.player = username; 
  38.  
  39. $scope.player_score = 0; 
  40.  
  41. me.room.findAll({room: room, type: 'user'}).fetch().subscribe(function(row){ 
  42.  
  43. var user_count = row.length; 
  44.  
  45. if(user_count == 2){ 
  46.  
  47. alert('Sorry, room is already full.'); 
  48.  
  49. }else
  50.  
  51. me.piece = 'X'
  52.  
  53. if(user_count == 1){ 
  54.  
  55. me.piece = 'O'
  56.  
  57.  
  58. me.room.store({ 
  59.  
  60. id: id, 
  61.  
  62. room: room, 
  63.  
  64. type: 'user'
  65.  
  66. name: username, 
  67.  
  68. piece: me.piece 
  69.  
  70. }); 
  71.  
  72. $scope.has_joined = true
  73.  
  74. me.room.findAll({room: room, type: 'user'}).watch().subscribe( 
  75.  
  76. function(users){ 
  77.  
  78. users.forEach(function(user){ 
  79.  
  80. if(user.id != me.id){ 
  81.  
  82. $scope.$apply(function(){ 
  83.  
  84. $scope.opponent = user.name
  85.  
  86. $scope.opponent_piece = user.piece; 
  87.  
  88. $scope.opponent_score = 0; 
  89.  
  90. }); 
  91.  
  92.  
  93. }); 
  94.  
  95. }, 
  96.  
  97. function(err){ 
  98.  
  99. console.log(err); 
  100.  
  101.  
  102. ); 
  103.  
  104. me.room.findAll({room: room, type: 'move'}).watch().subscribe( 
  105.  
  106. function(moves){ 
  107.  
  108. moves.forEach(function(item){ 
  109.  
  110. var block = document.getElementById(item.block); 
  111.  
  112. block.innerHTML = item.piece; 
  113.  
  114. block.className = "col done"
  115.  
  116. }); 
  117.  
  118. me.updateScores(); 
  119.  
  120. }, 
  121.  
  122. function(err){ 
  123.  
  124. console.log(err); 
  125.  
  126.  
  127. ); 
  128.  
  129.  
  130. }); 
  131.  
  132.  
  133. $scope.placePiece = function(id){ 
  134.  
  135. var block = document.getElementById(id); 
  136.  
  137. if(!angular.element(block).hasClass('done')){ 
  138.  
  139. me.room.store({ 
  140.  
  141. type: 'move'
  142.  
  143. room: me.room_name, 
  144.  
  145. block: id, 
  146.  
  147. piece: me.piece 
  148.  
  149. }); 
  150.  
  151.  
  152. }; 
  153.  
  154. me.updateScores = function(){ 
  155.  
  156. const possible_combinations = [ 
  157.  
  158. [1, 4, 7], 
  159.  
  160. [2, 5, 8], 
  161.  
  162. [3, 2, 1], 
  163.  
  164. [4, 5, 6], 
  165.  
  166. [3, 6, 9], 
  167.  
  168. [7, 8, 9], 
  169.  
  170. [1, 5, 9], 
  171.  
  172. [3, 5, 7] 
  173.  
  174. ]; 
  175.  
  176. var scores = {'X': 0, 'O': 0}; 
  177.  
  178. possible_combinations.forEach(function(row, row_index){ 
  179.  
  180. var pieces = {'X' : 0, 'O': 0}; 
  181.  
  182. row.forEach(function(id, item_index){ 
  183.  
  184. var block = document.getElementById(id); 
  185.  
  186. if(angular.element(block).hasClass('done')){ 
  187.  
  188. var piece = block.innerHTML; 
  189.  
  190. pieces[piece] += 1; 
  191.  
  192.  
  193. }); 
  194.  
  195. if(pieces['X'] == 3){ 
  196.  
  197. scores['X'] += 1; 
  198.  
  199. }else if(pieces['O'] == 3){ 
  200.  
  201. scores['O'] += 1; 
  202.  
  203.  
  204. }); 
  205.  
  206. $scope.$apply(function(){ 
  207.  
  208. $scope.player_score = scores[me.piece]; 
  209.  
  210. $scope.opponent_score = scores[$scope.opponent_piece]; 
  211.  
  212. }); 
  213.  
  214.  
  215.  
  216. })(); 

現在,分析一下上面代碼。首先,設置默認狀態。其中,has_joined變量用于是否玩家已經進入某個房間。其次,ready變量用于確定是否用戶已經連接到Horizon服務器。當這個變量值為false時,還不能向用戶顯示應用程序的界面。

  1. $scope.has_joined = false
  2.  
  3. $scope.ready = false

連接到服務器的代碼如下:

  1. const horizon = Horizon({host: 'localhost:8181'}); 
  2.  
  3. horizon.onReady(function(){ 
  4.  
  5. $scope.$apply(function(){ 
  6.  
  7. $scope.ready = true
  8.  
  9. }); 
  10.  
  11. }); 
  12.  
  13. horizon.connect(); //connect to the server 

如我前面所提到的,Horizon服務器默認使用的是8181端口。這正是我們為什么使用local:8181作為端口的原因。如果你連接到一個遠程服務器,這應該對應于分配給服務器的IP地址或者域名。當用戶連接到服務器時,onReady事件將會觸發。正是在此時,我們把ready設置為true,這樣就可以向用戶顯示程序的UI部分了。

  1. horizon.onReady(function(){ 
  2.  
  3. $scope.$apply(function(){ 
  4.  
  5. $scope.ready = true
  6.  
  7. }); 
  8.  
  9. }); 

進入房間

每當用戶點擊Join按鈕時,將執行join函數:

  1. $scope.join = function(username, room){ 
  2.  
  3. ... 
  4.  
  5. }; 

在此函數內部,連接到一個稱為tictactoe的集合。

【注意】因為我們處于開發模式下;所以,如果集合不存在的話,系統將自動為你創建。

  1. me.room = horizon('tictactoe'); 

接下來,生成一個ID,并把它設置為當前用戶的ID:

  1. var id = chance.integer({min: 10000, max: 999999}); 
  2.  
  3. me.id = id; 

接下來,設置玩家用戶名和默認的玩家得分。

  1. $scope.player = username; 
  2.  
  3. $scope.player_score = 0; 

【注意】這兩個變量都被綁定到模板中;所以,你可以隨時顯示與更新它們。

接下來,進行文檔查詢,查詢條件是:room屬性為當前房間且type屬性為user。千萬不要把這種查詢與subscribe函數弄混,在這里我們并不監聽數據變化的。而且,這里還要使用fetch函數;這意味著,只有在用戶進入一個房間時才執行該操作。相關代碼如下:

  1. me.room.findAll({room: room, type: 'user'}).fetch().subscribe(function(row){ 
  2.  
  3. ... 
  4.  
  5. }); 

一旦結果返回,即檢查用戶個數。當然,井字游戲只能由兩個玩家玩,所以,如果用戶想加入一個已經有兩名玩家的房間的話,系統會向他們發出警報。

  1. var user_count = row.length; 
  2.  
  3. if(user_count == 2){ 
  4.  
  5. alert('Sorry, room is already full.'); 
  6.  
  7. }else
  8.  
  9. ... 
  10.  

上面代碼中的else語句將繼續處理接受用戶的邏輯,即根據當前用戶數確定將被分配給用戶的卡片。第一個加入該房間的人得到"X"卡片,而第二個人得到"O"卡片。

  1. me.piece = 'X'
  2.  
  3. if(user_count == 1){ 
  4.  
  5. me.piece = 'O'
  6.  

一旦你選定了卡片,就把新用戶存儲到集合中,并把has_joined開關值取反,從而顯示井字棋盤。

  1. me.room.store({ 
  2.  
  3. id: id, 
  4.  
  5. room: room, 
  6.  
  7. type: 'user'
  8.  
  9. name: username, 
  10.  
  11. piece: me.piece 
  12.  
  13. }); 
  14.  
  15. $scope.has_joined = true

接下來,偵聽集合中的變化。這次,不是通過fetch方式,而是使用watch方式。具體地說,每當添加新文檔或更新(或刪除)匹配查詢的現有文檔時,都要執行回調函數。回調函數執行時,循環遍歷所有的結果并設置對手的詳細信息——如果該文檔的用戶ID與當前用戶的ID不匹配的話。本程序中正是通過這種方式來向當前用戶顯示他們的對手是誰。

  1. me.room.findAll({room: room, type: 'user'}).watch().subscribe( 
  2.  
  3. function(users){ 
  4.  
  5. users.forEach(function(user){ 
  6.  
  7. if(user.id != me.id){ 
  8.  
  9. $scope.$apply(function(){ 
  10.  
  11. $scope.opponent = user.name
  12.  
  13. $scope.opponent_piece = user.piece; 
  14.  
  15. $scope.opponent_score = 0; 
  16.  
  17. }); 
  18.  
  19.  
  20. }); 
  21.  
  22. }, 
  23.  
  24. function(err){ 
  25.  
  26. console.log(err); 
  27.  
  28.  
  29. ); 

接下來要訂閱move事件,該事件每當玩家把他們的卡片放到棋盤上從而這導致文檔變化時就觸發一次。如果發生這種情況,則遍歷所有的移動動作并將文本添加到相應的格子。從現在開始,代碼中將使用“block”一詞來表示棋盤上的每一個格子。

添加的文本對應于每個用戶所使用的卡片;此外,代碼中還將類名替換為“col done”。其中,col相應于Ionic編程中網格實現類,而done是用于表示一個特定塊上已經已經有一個卡片的類。如果用戶還能將卡片放在格子上,我們就使用這種辦法來檢查。在更新棋盤用戶界面后,通過調用updateScores函數(將在以后添加這個函數)來更新玩家的成績。

  1. me.room.findAll({room: room, type: 'move'}).watch().subscribe( 
  2.  
  3. function(moves){ 
  4.  
  5. moves.forEach(function(item){ 
  6.  
  7. var block = document.getElementById(item.block); 
  8.  
  9. block.innerHTML = item.piece; 
  10.  
  11. block.className = "col done"
  12.  
  13. }); 
  14.  
  15. me.updateScores(); 
  16.  
  17. }, 
  18.  
  19. function(err){ 
  20.  
  21. console.log(err); 
  22.  
  23.  
  24. ); 

放置卡片

每當用戶點擊棋盤上的任何一格時都要調用placePiece函數,同時要提供對應格子的ID值作為此函數的參數。這允許我們隨心所欲地操縱游戲格子。在本程序中,使用此函數來檢查某個游戲格子是否屬于done類型。如果沒有done標志,則創建一個新的移動,并顯示當前房間、格子ID值及對應的卡片。

  1. $scope.placePiece = function(id){ 
  2.  
  3. var block = document.getElementById(id); 
  4.  
  5. if(!angular.element(block).hasClass('done')){ 
  6.  
  7. me.room.store({ 
  8.  
  9. type: 'move'
  10.  
  11. room: me.room_name, 
  12.  
  13. block: id, 
  14.  
  15. piece: me.piece 
  16.  
  17. }); 
  18.  
  19.  
  20. }; 

更新玩家得分

為了更新玩家得分,需要構建一個包含可能的獲勝組合的數組,如下所示:

  1. const possible_combinations = [ 
  2.  
  3. [1, 4, 7], 
  4.  
  5. [2, 5, 8], 
  6.  
  7. [3, 2, 1], 
  8.  
  9. [4, 5, 6], 
  10.  
  11. [3, 6, 9], 
  12.  
  13. [7, 8, 9], 
  14.  
  15. [1, 5, 9], 
  16.  
  17. [3, 5, 7] 
  18.  
  19. ]; 

在這段代碼中,[1, 4, 7]對應于第一行,[1, 2, 3]對應于第一列。只要相應的數字存在,順序是無關緊要的。下面的圖形有助于你更直觀地了解這一點。

 

接下來,需要初始化每個單獨卡片的得分并循環遍歷每個可能的組合。對于每一次循環遍歷,初始化已經放到棋盤上的卡片總數。然后針對每一種可能的組合進行循環遍歷。使用id來檢查是否相應的格子上已經放了卡片。如果已經有了卡片,則取得實際卡片并把卡片總數加1。在循環結束后,檢查是否卡片總數等于3。如果卡片總數等于3,則增加該卡片得分數,直到遍歷完所有可能的組合。一旦完成,更新當前玩家和對手的得分值。

  1. var scores = {'X': 0, 'O': 0}; 
  2.  
  3. possible_combinations.forEach(function(row, row_index){ 
  4.  
  5. var pieces = {'X' : 0, 'O': 0}; 
  6.  
  7. row.forEach(function(id, item_index){ 
  8.  
  9. var block = document.getElementById(id); 
  10.  
  11. if(angular.element(block).hasClass('done')){ //check if there's already a piece 
  12.  
  13. var piece = block.innerHTML; 
  14.  
  15. pieces[piece] += 1; 
  16.  
  17.  
  18. }); 
  19.  
  20. if(pieces['X'] == 3){ 
  21.  
  22. scores['X'] += 1; 
  23.  
  24. }else if(pieces['O'] == 3){ 
  25.  
  26. scores['O'] += 1; 
  27.  
  28.  
  29. }); 
  30.  
  31. //update current player and opponent score 
  32.  
  33. $scope.$apply(function(){ 
  34.  
  35. $scope.player_score = scores[me.piece]; 
  36.  
  37. $scope.opponent_score = scores[$scope.opponent_piece]; 
  38.  
  39. }); 

創建主模板文件

現在,在目錄www/templates下創建一個模板文件home.html,并添加如下代碼:

  1. <ion-view title="Home" ng-controller="HomeController as home_ctrl" ng-init="connect()"
  2.  
  3. <header class="bar bar-header bar-stable"
  4.  
  5. <h1 class="title">Ionic Horizon Tic Tac Toe</h1> 
  6.  
  7. </header> 
  8.  
  9. <ion-content class="has-header" ng-show="home_ctrl.ready"
  10.  
  11. <div id="join" class="padding" ng-hide="home_ctrl.has_joined"
  12.  
  13. <div class="list"
  14.  
  15. <label class="item item-input"
  16.  
  17. <input type="text" ng-model="home_ctrl.room" placeholder="Room Name"
  18.  
  19. </label> 
  20.  
  21. <label class="item item-input"
  22.  
  23. <input type="text" ng-model="home_ctrl.username" placeholder="User Name"
  24.  
  25. </label> 
  26.  
  27. </div> 
  28.  
  29. <button class="button button-positive button-block" ng-click="join(home_ctrl.username, home_ctrl.room)"
  30.  
  31. join 
  32.  
  33. </button> 
  34.  
  35. </div> 
  36.  
  37. <div id="game" ng-show="home_ctrl.has_joined"
  38.  
  39. <div id="board"
  40.  
  41. <div class="row"
  42.  
  43. <div class="col" ng-click="placePiece(1)" id="1"></div> 
  44.  
  45. <div class="col" ng-click="placePiece(2)" id="2"></div> 
  46.  
  47. <div class="col" ng-click="placePiece(3)" id="3"></div> 
  48.  
  49. </div> 
  50.  
  51. <div class="row"
  52.  
  53. <div class="col" ng-click="placePiece(4)" id="4"></div> 
  54.  
  55. <div class="col" ng-click="placePiece(5)" id="5"></div> 
  56.  
  57. <div class="col" ng-click="placePiece(6)" id="6"></div> 
  58.  
  59. </div> 
  60.  
  61. <div class="row"
  62.  
  63. <div class="col" ng-click="placePiece(7)" id="7"></div> 
  64.  
  65. <div class="col" ng-click="placePiece(8)" id="8"></div> 
  66.  
  67. <div class="col" ng-click="placePiece(9)" id="9"></div> 
  68.  
  69. </div> 
  70.  
  71. </div> 
  72.  
  73. <div id="scores"
  74.  
  75. <div class="row"
  76.  
  77. <div class="col col-50 player"
  78.  
  79. <div class="player-name" ng-bind="player"></div> 
  80.  
  81. <div class="player-score" ng-bind="player_score"></div> 
  82.  
  83. </div> 
  84.  
  85. <div class="col col-50 player"
  86.  
  87. <div class="player-name" ng-bind="opponent"></div> 
  88.  
  89. <div class="player-score" ng-bind="opponent_score"></div> 
  90.  
  91. </div> 
  92.  
  93. </div> 
  94.  
  95. </div> 
  96.  
  97. </div> 
  98.  
  99. </ion-content> 
  100.  
  101. </ion-view

現在,我們來分析一下上面的代碼。首先,創建了一個總的包裝器,在用戶連接到Horizon服務器前這部分內容是不顯示的:

  1. <ion-content class="has-header" ng-show="home_ctrl.ready"
  2.  
  3. ... 
  4.  
  5. </ion-content> 

加入游戲房間的表單代碼如下:

  1. <div id="join" class="padding" ng-hide="home_ctrl.has_joined"
  2.  
  3. <div class="list"
  4.  
  5. <label class="item item-input"
  6.  
  7. <input type="text" ng-model="home_ctrl.room" placeholder="Room Name"
  8.  
  9. </label> 
  10.  
  11. <label class="item item-input"
  12.  
  13. <input type="text" ng-model="home_ctrl.username" placeholder="User Name"
  14.  
  15. </label> 
  16.  
  17. </div> 
  18.  
  19. <button class="button button-positive button-block" ng-click="join(home_ctrl.username, home_ctrl.room)"
  20.  
  21. join 
  22.  
  23. </button> 
  24.  
  25. </div> 

井字棋棋盤的設計相關代碼如下:

  1. <div id="board"
  2.  
  3. <div class="row"
  4.  
  5. <div class="col" ng-click="placePiece(1)" id="1"></div> 
  6.  
  7. <div class="col" ng-click="placePiece(2)" id="2"></div> 
  8.  
  9. <div class="col" ng-click="placePiece(3)" id="3"></div> 
  10.  
  11. </div> 
  12.  
  13. <div class="row"
  14.  
  15. <div class="col" ng-click="placePiece(4)" id="4"></div> 
  16.  
  17. <div class="col" ng-click="placePiece(5)" id="5"></div> 
  18.  
  19. <div class="col" ng-click="placePiece(6)" id="6"></div> 
  20.  
  21. </div> 
  22.  
  23. <div class="row"
  24.  
  25. <div class="col" ng-click="placePiece(7)" id="7"></div> 
  26.  
  27. <div class="col" ng-click="placePiece(8)" id="8"></div> 
  28.  
  29. <div class="col" ng-click="placePiece(9)" id="9"></div> 
  30.  
  31. </div> 
  32.  
  33. </div> 

玩家得分部分對應的代碼如下:

  1. <div id="scores"
  2.  
  3. <div class="row"
  4.  
  5. <div class="col col-50 player"
  6.  
  7. <div class="player-name" ng-bind="player"></div> 
  8.  
  9. <div class="player-score" ng-bind="player_score"></div> 
  10.  
  11. </div> 
  12.  
  13. <div class="col col-50 player"
  14.  
  15. <div class="player-name" ng-bind="opponent"></div> 
  16.  
  17. <div class="player-score" ng-bind="opponent_score"></div> 
  18.  
  19. </div> 
  20.  
  21. </div> 
  22.  
  23. </div> 

編寫樣式文件

下面給出客戶端應用程序的樣式定義:

  1. #board .col { 
  2.  
  3. text-align: center; 
  4.  
  5. height: 100px; 
  6.  
  7. line-height: 100px; 
  8.  
  9. font-size: 30px; 
  10.  
  11. padding: 0; 
  12.  
  13.  
  14. #board .col:nth-child(2) { 
  15.  
  16. border-right: 1px solid; 
  17.  
  18. border-left: 1px solid; 
  19.  
  20.  
  21. #board .row:nth-child(2) .col { 
  22.  
  23. border-top: 1px solid; 
  24.  
  25. border-bottom: 1px solid; 
  26.  
  27.  
  28. .player { 
  29.  
  30. font-weight: bold; 
  31.  
  32. text-align: center; 
  33.  
  34.  
  35. .player-name { 
  36.  
  37. font-size: 18px; 
  38.  
  39.  
  40. .player-score { 
  41.  
  42. margin-top: 15px; 
  43.  
  44. font-size: 30px; 
  45.  
  46.  
  47. #scores { 
  48.  
  49. margin-top: 30px; 
  50.  

運行應用程序

現在,你可以通過執行應用程序根目錄下的如下命令在你的瀏覽器中測試應用程序:

  1. ionic serve 

這樣啟動的服務器端將服務于本地項目并在你的默認瀏覽器中打開一個新的選項卡。

如果你想要和朋友一起測試的話,你可以使用Ngrok把Horizon服務器發布到互聯網上,命令如下:

  1. ngrok http 8181 

這個命令將生成一個URL,當你連接到Horizon服務器時可以用作主機地址:

  1. const horizon = Horizon({host: 'xxxx.ngrok.io'}); 

除此之外,還要在index.html文件中改變到horizon.js文件的引用:

  1. <script src="http://xxxx.ngrok.io/horizon/horizon.js"></script> 

若要創建程序的移動版本,需要在你的項目中添加一個平臺(例如,安卓系統)。這假定你已經在自己的計算機上安裝了Android SDK。

  1. ionic platform add android 

接下來,我們生成.apk文件,命令如下:

  1. ionic build android 

到此,你可以把這個.apk文件發送給你的朋友來一起把玩這個游戲。當然,你也可以自己玩這個游戲,這全是你自己的事了。

小結

在本教程中,你開發的僅僅是一個再簡單不過的應用程序;因此,還有很多方面加以適當改進的話,效果會更好。下面列舉的是供你參考改進的幾個內容,把這些當成你的技能作業好了。

再開發一個4×4或5×5的版本:目前你開發出的3×3版本幾乎總是會導致僵局,尤其是如果兩名玩井字游戲的玩家都是專家級的話。

得分邏輯:你不得不通過大量的循環來取得玩家得分。也許你可以想出更好的方案來實現。

美化游戲風格:當前游戲的風格十分平實,其實它模擬的是適用于在紙上玩的井字游戲。

添加動畫:當用戶加入一個房間時,你可以嘗試為棋盤添加“滑落”動畫效果;或者當玩家把卡片放到棋盤上時實現“彈出”動畫效果。您可以使用animate.css文件來實現這類動畫。

添加SNS登錄支持:在這么簡單的一個應用中添加SNS功能恐怕要求有點過了,但如果你想要了解如何在Horizon框架中實現身份驗證工作原理,這倒是一個相當不錯的鍛煉機會。使用Horizon認證,你可以讓用戶登錄其Facebook、Twitter或Github帳戶。

添加再玩一次功能:游戲完畢后你可以嘗試添加一個“Play Again”按鈕。按下這個按鈕時,系統將清除排行榜和得分,以便玩家可以再玩一次。

添加實時排行榜功能:添加比賽排行榜來顯示誰贏了比賽。

【51CTO譯稿,合作站點轉載請注明原文譯者和出處為51CTO.com】

 

責任編輯:趙立京 來源: 51CTO
相關推薦

2016-06-23 13:56:48

云計算應用后端

2011-11-23 10:06:32

Azure微軟移動應用

2012-03-31 19:53:02

惠普應用變革移動

2013-02-21 09:27:26

2020-02-12 09:00:48

數據網格Apache Igni數據管理

2011-11-04 11:38:32

RadwareJuniper應用交付

2014-09-03 21:49:57

移動VPN解決方案

2023-04-18 08:21:23

KafkaApache預訂應用

2011-09-14 17:08:08

VMware

2017-07-10 17:25:40

開發單號高可用

2023-07-26 16:20:36

云原生云計算

2012-06-13 00:00:00

云計算NoSQLPHP

2012-03-29 10:45:40

惠普應用服務移動應用

2024-02-26 00:01:01

RedisGolang應用程序

2016-08-24 19:22:10

Docker SwarPython應用

2012-11-14 15:25:58

2024-05-10 13:14:41

技巧云原生應用

2013-12-27 16:04:25

2009-03-16 09:16:13

行為擴展WCF.NET

2020-06-02 09:45:07

微前端組件代碼
點贊
收藏

51CTO技術棧公眾號

主站蜘蛛池模板: 日日夜夜精品 | 丝袜 亚洲 欧美 日韩 综合 | 精品一区二区三区免费视频 | 在线观看特色大片免费网站 | 日日骚av | 国产一区中文 | 国产在线色 | 成人精品一区二区三区中文字幕 | 一区二区三区成人 | 成人国产午夜在线观看 | 视频一区二区在线观看 | 久久国产精品精品国产色婷婷 | 亚洲 日本 欧美 中文幕 | 永久免费视频 | 国内自拍第一页 | 国产福利视频 | 在线观看免费av片 | 精品1区2区3区4区 | 久久精品色视频 | 日韩精品久久久久 | 范冰冰一级做a爰片久久毛片 | 欧美激情一区二区三区 | 激情网站 | 久久久久久国产 | 日韩中文一区 | 中文字幕av亚洲精品一部二部 | av网站在线免费观看 | 久久久久久成人 | 在线中文字幕视频 | 久久狼人天堂 | 国产成人精品免费 | 国产精品99久久久久久宅男 | 97视频成人 | 美女久久久久 | 久久中文高清 | 欧美一级www片免费观看 | jav成人av免费播放 | 国产精品一区二区三区四区 | 亚卅毛片| a级毛片毛片免费观看久潮喷 | 一区二区三区国产 |