Flip The Card(プログラム)
「index.html」のコード全体は下記になります。
<!DOCTYPE html> <html lang="ja"> <head> <meta charset="UTF-8"> <title>カードゲームアプリ</title> <link rel="stylesheet" href="common.css"> </head> <body> <div id="modal_frame"> <div id="modal_box"> <p>ゲーム終了!!</p> <p>正解率:<span id="success_rate">0</span>%</p> <button id="retry_btn">リトライ</button> <button class="to_start_btn">スタート画面へ</button> </div> </div> <header> <img src="./images/title.png" alt="カードゲームタイトル"> </header> <main> <div id="start_screen"> <p class="rank_16_frame">16マスの現在のランク:<span class="rank_16"></span></p> <p class="rank_36_frame">36マスの現在のランク:<span class="rank_36"></span></p> <div id="select_game_mode_frame"> <p>■ゲームモード選択</p> <div> <p>マス目の数を選択してください。</p> <select id="game_mode"> <option value="16">16マス</option> <option value="36">36マス</option> </select> </div> </div> <button id="start_btn">スタート</button> <hr> <button id="score_btn">スコア&ランク</button> </div> <div id="game_screen"> <p id="message"></p> <p>現在のカード選択回数:<span id="select_card_count">0</span></p> <div id="cards_container" class="clear"></div> <button id="next_btn">次へ</button> </div> <div id="score_screen"> <p id="score_title">スコア&ランク</p> <p class="rank_16_frame">16マスの現在のランク:<span class="rank_16"></span></p> <table id="score_16_list"></table> <hr> <p class="rank_36_frame">36マスの現在のランク:<span class="rank_36"></span></p> <table id="score_36_list"></table> <hr> <button class="to_start_btn">スタート画面</button> </div> </main> <script> let firstCard = null; //1回目に選択したカード let secondCard = null; //2回目に選択したカード let cardsNums = new Array(); //カードの番号用配列 let firstPickFlg = true; //1回目のカードの選択フラグ let finishedOneCycle = false; //1サイクル文のゲーム終了フラグ let cardsObj = null; //全カードオブジェクト格納用 let successCount = 0; //カード選択成功回数 let selectCount = 0; //カード選択回数 let game_mode_num = 16; //ゲームモード let score_16_data = new Array(); //16マスのスコアデータ let score_36_data = new Array(); //36マスのスコアデータ /** * カードを生成 */ function createCards() { getElmId("cards_container").innerHTML = ''; //カードをクリア cardsNums = createRandomNums(); //カードのランダムな番号を生成 for (var i = 0; i < game_mode_num; i++) { let frame = document.createElement("div"); //カードフレームの作成 let card = document.createElement("div"); //カードの作成 let card_num = document.createElement("span"); //カード番号用 let card_cover_img = document.createElement("img"); //カバー画像表示用 card_cover_img.dataset.id = i; //データIDを設定 card_cover_img.src = "images/cover.png"; //カバー画像ファイル名を設定 card.classList.add("card"); //クラスを追加 card_num.classList.add("card_num"); //クラスを追加 card_num.style.display = "none"; //カードの番号を非表示 card.appendChild(card_num); //カード番号をカードフレームに追加 card.appendChild(card_cover_img); //カバー画像をカードフレームに追加 frame.appendChild(card); //カードフレームへカードを追加 getElmId("cards_container").appendChild(frame); //カードコンテナにカードフレームを追加 } let cards = Array.from(document.getElementsByClassName("card")); //カードを配列として取得 cards.forEach(element => { element.addEventListener("click", selectCard, false); //カードクリック時のイベントリスナーを設定 }); //カードの横幅を設定 let cards_frame = Array.from(getElmId('cards_container').children); //全カード要素を取得 if (game_mode_num === 16) { //16マスのゲームの場合 cards_frame.forEach( element => element.style.width = "25%" ); } else if (game_mode_num === 36) { //36マスのゲームの場合 cards_frame.forEach( element => element.style.width = "16%" ); } } /** * カード選択時の処理 */ function selectCard(e) { if (!finishedOneCycle) { //1サイクルのゲームが終了していない場合 selectCount++; //カード選択カウントを+1カウントアップ getElmId('select_card_count').innerHTML = selectCount; //カード選択回数を表示 let selectCard = cardsObj[e.target.dataset.id]; //選択カードの取得 selectCard.getElementsByTagName('img')[0].style.display = "none"; //カバー画像を非表示 selectCard.getElementsByTagName('span')[0].style.display = "block"; //カード番号用要素を表示 selectCard.getElementsByTagName('span')[0].innerHTML = cardsNums[e.target.dataset.id]; //カード番号を設定 if (firstPickFlg) { //1回目のカードを選択しているか? firstCard = selectCard; //1回目の「選択カード」を変数に設定 firstPickFlg = false; //1回目の「カード選択フラグ」を設定 getElmId("message").innerHTML = "2枚目のカードを選択してください。"; //表示メッセージを設定 } else { //2回目のカードを選択しているか? getElmId("next_btn").disabled = false; //「次へ」ボタンを有効化 getElmId("next_btn").style.backgroundColor = "#0000ff"; //「次へ」ボタンの背景色を青に変更 secondCard = selectCard; //2回目の選択カードを変数に設定 firstPickFlg = true; //1回目のカード選択フラグを設定 finishedOneCycle = true; //1サイクル分のゲーム実行フラグを設定 getElmId("message").innerHTML = "「次へ」ボタンをクリックしてください。"; //表示メッセージを設定 if (successCount === (game_mode_num / 2) - 1) { //全てのカードが正解になっているか? //「ゲーム結果」をローカルストレージへ保存 var d = new Date(); //日付オブジェクトを生成 //日付文字列を生成 var d_save_str = d.getFullYear() + "年" + (d.getMonth() + 1) + "月" + d.getDate() + "日" + d.getHours() + "時" + d.getMinutes() + "分"; //「配列データ」を作成 let regist_data = { 'select_count': selectCount, 'date': d_save_str } if (game_mode_num === 16) { //16マスのゲーム実行時 score_16_data.push(regist_data); //スコアデータをスコア用配列に追加 saveScoreData('score_16_data', score_16_data); //スコア用配列をローカルストレージへ保存 } else if (game_mode_num === 36) { //36マスのゲーム実行時 score_36_data.push(regist_data); //スコアデータをスコア用配列に追加 saveScoreData('score_36_data', score_36_data); //スコア用配列をローカルストレージへ保存 } getElmId("message").innerHTML = "終了"; //メッセージを表示 getElmId("next_btn").disabled = true; //「次へ」ボタンを無効化 getElmId("next_btn").style.backgroundColor = "#afafaf"; //「次へ」ボタンの背景色を変更 getElmId('modal_frame').style.display = 'block'; //モーダル画面を表示 getElmId('success_rate').innerHTML = Math.round((16 / selectCount) * 100); //モーダル画面へ正解率を表示 successCount = 0; //正解率をリセット selectCount = 0; //カード選択回数をリセット finishedOneCycle = false; //1サイクル分のゲーム実行フラグを設定 } } } else { alert("「次へ」ボタンをクリックしてください。"); } } /** * 次のカードを選択 */ function nextSelectCard() { getElmId("message").innerHTML = "1枚目のカードを選択してください。"; //表示メッセージを設定 //選択カード間違い時 if (firstCard.getElementsByTagName('span')[0].innerHTML !== secondCard.getElementsByTagName('span')[0].innerHTML) { firstCard.getElementsByTagName('img')[0].style.display = "block"; //1回目の選択カードのカバー画像を表示 firstCard.getElementsByTagName('span')[0].style.display = "none"; //1回目の選択カードの番号を非表示 secondCard.getElementsByTagName('img')[0].style.display = "block"; //2回目の選択カードのカバー画像を表示 secondCard.getElementsByTagName('span')[0].style.display = "none"; //2回目の選択カードの番号を非表示 } else { //選択カード正解時 successCount++; //正解カウントを1カウントアップ } finishedOneCycle = false; //1サイクル分のゲーム実行フラグを設定 getElmId("next_btn").disabled = true; //「次へ」ボタンを無効化 getElmId("next_btn").style.backgroundColor = "#afafaf"; //「次へ」ボタンの背景色を変更 } /** * ランダム値のペアを生成 */ function createRandomNums() { let randNums = []; //ランダム値格納用 let nums = []; //配列要素番号格納用 let rand = 0; //ランダム値格納用 //配列要素番号を作成 for (let i = 1; i <= (game_mode_num / 2); i++) { nums.push(i); nums.push(i); } //ランダムな配列要素番号を作成 for (let i = 0; i < game_mode_num; i++) { rand = Math.floor(Math.random() * nums.length); randNums.push(nums[rand]); nums.splice(rand, 1); } return randNums; } /** * 「スタート」ボタンをクリック時 */ function startGame() { displayScreen('game'); //ゲーム画面を表示 getElmId("select_card_count").innerHTML = selectCount; //カード選択回数を更新 game_mode_num = parseInt(getElmId('game_mode').value); //ゲームモードを取得 createCards(); //カードの生成 cardsObj = getElmClass("card"); //カードオブジェクトを取得 getElmId("message").innerHTML = "1枚目のカードを選択してください。"; //表示メッセージを設定 getElmId("next_btn").disabled = true; //「次へ」ボタンを無効化 } /** * 「スコア」ボタンをクリック時 */ function displayScore() { displayScreen('score'); //スコア画面を表示 displayScoreList(); //スコア一覧を表示 } /** * スコア一覧を表示 */ function displayScoreList() { //16マスのランクを表示 let score_16_rank = getRankVal(score_16_data); //「ランク値」を計算 Array.from(getElmClass('rank_16')).forEach( element => isNaN(score_16_rank) ? element.innerHTML = score_16_rank : element.innerHTML = createRankStar(score_16_rank) ); //36マスのランクを表示 let score_36_rank = getRankVal(score_36_data); //「ランク値」を計算 Array.from(getElmClass('rank_36')).forEach( element => isNaN(score_36_rank) ? element.innerHTML = score_36_rank : element.innerHTML = createRankStar(score_36_rank) ); //16マスの「スコア一覧」テーブルを作成&表示 createScoreTable('score_16_list', score_16_data); //36マスの「スコア一覧」テーブルを作成&表示 createScoreTable('score_36_list', score_36_data); } /** * 「スコア一覧」テーブルを作成&表示(最新のデータから10プレイ分を表示) */ function createScoreTable(list_type, score_data) { getElmId(list_type).innerHTML = ''; //現在の表示内容をクリア //テーブルヘッダーの作成&表示 let tr = document.createElement('tr'); let td = null; let th = document.createElement('th'); th.innerHTML = 'プレイ日時'; tr.appendChild(th); th = document.createElement('th'); th.innerHTML = '正解率'; tr.appendChild(th); getElmId(list_type).appendChild(tr); if (score_data.length !== 0) { //スコア一覧データを作成&表示 for (var i = score_data.length - 1; i > (score_data.length - 1) - 10; i--) { if (i >= 0) { tr = document.createElement('tr'); td = document.createElement('td'); td.innerHTML = score_data[i]['date']; tr.appendChild(td); td = document.createElement('td'); td.innerHTML = Math.round((16 / parseInt(score_data[i]['select_count'])) * 100) + "%"; tr.appendChild(td) getElmId(list_type).appendChild(tr); } } } } /** * 指定画面を表示 */ function displayScreen(screen_type) { hideScreen(); switch (screen_type) { case 'start': //スタート画面を表示 getElmId('start_screen').style.display = 'block'; displayScoreList(); //スコア一覧を表示 break; case 'game': //ゲーム画面を表示 getElmId('game_screen').style.display = 'block'; break; case 'score': //スコア一覧画面を表示 getElmId('score_screen').style.display = 'block'; break; } } /** * 画面を非表示 */ function hideScreen() { getElmId('start_screen').style.display = 'none'; //スタート画面を非表示 getElmId('game_screen').style.display = 'none'; //ゲーム画面を非表示 getElmId('score_screen').style.display = 'none'; //スコア画面を非表示 } /** * 「リトライ」ボタンをクリック時(モーダル画面) */ function retry() { getElmId('modal_frame').style.display = 'none'; //モーダル画面を非表示 startGame(); //ゲームを開始 } /** * 「スタート画面へ」ボタンをクリック時(モーダル画面) */ function toStartScreen() { getElmId('modal_frame').style.display = 'none'; //モーダル画面を非表示 displayScreen('start'); //スタート画面を表示 } /** * 「ランク値」を計算 */ function getRankVal(scoreData) { if (scoreData.length < 10) { //プレイ回数が足りない場合 let remain_game = 10 - scoreData.length; //スコア判定までの残りゲーム数 return 'あと' + remain_game + 'ゲームでランクが判定できます。'; //表示用メッセージの作成 } else { let sum = 0; //合計値格納用 let rank = 0; //ランク数格納用 //最新のデータから10プレイ分を元に「ランク値」を算出 for (var i = scoreData.length - 1; i > (scoreData.length - 1) - 10; i--) { sum += parseInt(scoreData[i]['select_count']); //スコアの合計値を計算 } let judge_val = Math.round((160 / sum) * 100); //スコアを算出 rank = Math.floor((judge_val / 10) + 1); //ランクの計算 return rank; } } /** * 「ランクスター」を作成 */ function createRankStar(rank_val) { let rank_star = ''; //ランクスター文字列格納用 for (var i = 1; i <= 10; i++) { i <= rank_val ? rank_star += "★" : rank_star += "☆"; //ランクスター文字列を作成 } //表示用ランク文字列を作成 return '<span class="low_label">Low</span> ' + rank_star + ' <span class="high_label">High</span> (Rank:' + rank_val + ')'; } /** * 「スコアデータ」をロード */ function loadScoreData(data_type) { let load_json = localStorage.getItem(data_type); //「ローカルストレージ」からデータをロード let score_data = JSON.parse(load_json); //「JSON」から「スコアデータ(配列)」へ変換 //スコアデータが無い場合 if (score_data === null) { score_data = new Array(); } return score_data; } /** * 「スコアデータ」をセーブ */ function saveScoreData(data_type, save_data) { let save_json = JSON.stringify(save_data); //「スコアデータ(配列)」を「JSON」へ変換 localStorage.setItem(data_type, save_json); //「ローカルストレージ」へセーブ } /** * 指定したIDの要素を取得 */ function getElmId(val) { return document.getElementById(val); } /** * 指定したクラスの要素を取得 */ function getElmClass(val) { return document.getElementsByClassName(val); } /** * 指定した要素名の要素を取得 */ function getElm(val) { return document.querySelector(val); } window.onload = function () { getElmId("start_btn").addEventListener("click", startGame, false); //スタートボタンのイベントリスナー設定 getElmId("next_btn").addEventListener("click", nextSelectCard, false); //「次へ」ボタンのイベントリスナー設定 getElmId("retry_btn").addEventListener("click", retry, false); //リトライボタンのイベントリスナー設定 let to_start_btn = Array.from(getElmClass('to_start_btn')); //スタート画面遷移ボタンの取得 to_start_btn.forEach(element => { element.addEventListener("click", toStartScreen, false); //スタート画面遷移ボタンのイベントリスナー設定 }); getElmId("score_btn").addEventListener("click", displayScore, false); //スコアボタンのイベントリスナー設定 // スコアデータのロード score_16_data = loadScoreData('score_16_data'); //16マスのスコアデータをロード score_36_data = loadScoreData('score_36_data'); //36マスのスコアデータをロード displayScreen('start'); //スタート画面を表示 getElmId('modal_frame').style.display = 'none'; //モーダル画面を非表示 } </script> </body> </html>
「common.css」のコード全体は下記になります。
body { margin: 0; } header { margin: 10px 0 20px 0; } hr { margin: 30px 0; } header img, #start_screen, #score_screen, #game_screen, #score_16_list, #score_36_list, #select_game_mode_frame { max-width: 600px; min-width: 300px; } header img, #start_screen, #score_screen, #game_screen, #start_btn, #score_btn, #start_screen select, .card>span, #retry_btn, .to_start_btn, #next_btn, #modal_box button { display: block; } #select_game_mode_frame>p, #score_title { background: -moz-linear-gradient(#5f5f5f, #ffffff); background: -webkit-linear-gradient(#5f5f5f, #ffffff); background: linear-gradient(#5f5f5f, #ffffff); } header img, #start_screen, #score_screen, #game_screen { width: 100%; margin: 0 auto; padding: 5px; } #start_btn, #score_btn { width: 200px; padding: 10px 0; margin: 0 auto; } #select_game_mode_frame>p { color: #ffffff; font-weight: bold; margin: 10px 0 0 0; padding: 15px 10px 15px 10px; border: solid 1px #bfbfbf; border-radius: 10px 10px 0 0; } #select_game_mode_frame>div { margin: 0 0 10px 0; padding: 10px 10px 15px 10px; border: solid 1px #bfbfbf; border-top: none; border-radius: 0 0 10px 10px; } #start_screen select { width: 155px; margin: 10px 0; padding: 6px 0 10px 12px; border: solid 1px #bfbfbf; border-radius: 30px; font-size: 18px; } #message { font-size: 24px; font-weight: bold; color: #ffffff; text-align: center; border-top: solid 2px #0000ff; border-bottom: solid 2px #0000ff; border-right: solid 2px #0000ff; border-left: solid 2px #0000ff; margin: 10px 0; padding: 5px 0; background: -moz-radial-gradient(#0000ff, #ffffff); background: -webkit-radial-gradient(#0000ff, #ffffff); background: radial-gradient(#0000ff, #ffffff); } #cards_container { margin: 0 auto; border: solid 1px #dfdfdf; } #cards_container>div { width: 25%; height: 100px; padding: 5px; float: left; box-sizing: border-box; } .card { text-align: center; width: 100%; height: 100%; border: solid 1px #bfbfbf; } .card>span { padding-top: 28px; font-size: 24px; } .card>img { width: 100%; height: 100%; } .rank_16_frame, .rank_36_frame { border: solid 1px #bfbfbf; padding: 15px 10px; border-radius: 25px; box-shadow: 0 5px 5px 0 rgba(0, 0, 0, .5); } .low_label { color: #ffffff; padding: 5px; background: -moz-radial-gradient(#8f8f8f, #ffffff); background: -webkit-radial-gradient(#8f8f8f, #ffffff); background: radial-gradient(#8f8f8f, #ffffff); border-radius: 15px; } .high_label { color: #ffffff; padding: 5px; background: -moz-radial-gradient(#ff0000, #ffffff); background: -webkit-radial-gradient(#ff0000, #ffffff); background: radial-gradient(#ff0000, #ffffff); border-radius: 7px; } #start_btn, #score_btn, #retry_btn, .to_start_btn { padding: 20px; border-radius: 35px; box-shadow: 0 5px 5px 0 rgba(0, 0, 0, .5); border: none; margin: 10px auto; } #next_btn { font-size: 20px; color: #ffffff; clear: both; margin: 20px auto 0 auto; padding: 20px 60px; border: none; border-radius: 40px; } .clear:after { content: ""; display: block; clear: both; } #score_title { color: #ffffff; font-size: 20px; font-weight: bold; text-align: center; border: solid 3px #bfbfbf; padding: 15px 0; } #modal_frame { background: rgba(45, 45, 45, 0.5); position: absolute; width: 100%; height: 100%; } #modal_box { width: 50%; background-color: #ffffff; border: solid 1px #8f8f8f; opacity: 1.0; margin: 30px auto 0 auto; padding: 50px; font-size: 20px; text-align: center; box-shadow: 0 15px 15px 0 rgba(0, 0, 0, .5); background-image: url("./images/finished_game_back.png"); background-repeat: no-repeat; } #modal_box p { color: #3f3f3f; font-weight: bold; } #modal_box button { width: 200px; padding: 10px 50px; margin: 20px auto; } table { border-collapse: collapse; margin-bottom: 30px; } #score_16_list, #score_36_list { margin: 30px auto 40px auto; border: solid 5px #5f5f5f; box-shadow: 0 15px 15px 0 rgba(0, 0, 0, .5); } #score_16_list td, #score_16_list th, #score_36_list td, #score_36_list th { color: 18px; border: solid 2px #2f2f2f; padding: 6px 20px; text-align: center; } #score_16_list th, #score_36_list th { color: #ffffff; background-color: #8f8f8f; background: -moz-linear-gradient(#8f8f8f, #cfcfcf); background: -webkit-linear-gradient(#8f8f8f, #cfcfcf); background: linear-gradient(#8f8f8f, #cfcfcf); } #testDisplay td { border: solid 1px #000000; text-align: right; }