素数大富豪を作ったのでJavaScriptでもつくってみようと考えています。カードゲームをJavaScriptでつくったことはあったよね?と思って過去記事を調べてみたけれども見つからない。実はカードゲームはC#でデスクトップアプリとして作ったことはあるけれどもJavaScriptで作ったことはありません。
ということで今回はweb版素数大富豪をつくる準備としてJavaScriptでカードを表示させます。まずはカードを表示させるためのクラスをつくります。管理人はクラスが大好きです。クラスは初心者には難しいからクラスをできるだけ使わない説明をしているという方もいるようですが、私はそうは思いませんね。不必要にクラスをつくります。
HTML部分
まずHTML部分を示します。Cardsというクラスのインスタンスを生成して、そこからカードを初期化するInitCards関数を呼び出しています。実際にカードが表示されるのはボタンがクリックされてShowAllCards関数が呼び出されるときです。
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 48 49 50 51 52 53 54 55 56 57 |
<!DOCTYPE html> <html lang="ja"> <head> <meta charset="UTF-8"> <title>カードを表示させる</title> <meta http-equiv="X-UA-Compatible" content="IE=edge"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> #body { background-color: #000000; } #table { width: 800px; height:800px; margin: 0 auto 0; background-color: #008000; } #show-all-button { margin: 30px 0 30px 30px; } </head> <body id = "body"> <div id = "table"> <button id = "show-all-button" onclick="ShowAllCards()">カードを表示する</button> </div> <script src="./card.js"></script> <script> const CardWidth = 80; const CardHeight = 110; // カードをつくる let cards = new Cards(document.getElementById('table')); cards.InitCards(); // すべてのカードを表示する function ShowAllCards(){ for(let number=1; number<=13; number++) { for(let suit = 0; suit < 4; suit++) { let card = cards.GetObjectFromNumber(suit, number); if(card != null) card.Move(number * 30, 120 * suit); } let card = cards.GetObjectFromNumber(Suit.joker, 1); card.Move(1 * 30, 120 * 4); card = cards.GetObjectFromNumber(Suit.joker, 2); card.Move(2 * 30, 120 * 4); } } </script> </body> </html> |
ではCardsクラスの説明をしたいのですが、そのまえにSuitクラスとCardクラスをつくります。CardクラスのインスタンスをCardsクラスで管理するという感じです。
Suitクラス
Suitクラスはカードのスート(素数大富豪ではスートはあまり関係ないのだが)を管理します。列挙体のような使い方をしています。enum型は列挙型ともよばれ、複数の定数をひとつにまとめ一連の値を付けることができる型のことですが、JavaScriptには残念ながらenum型はありません。ただ同じようなものは作ることができます。
card.js
1 2 3 4 5 6 7 |
class Suit{ static Club = 0; static Diamond = 1; static Heart = 2; static Spade = 3; static joker = 4; } |
クラスを使わずに辞書型を使う方法もあります。
card.js
1 2 3 4 5 6 7 |
let Suit = { Club : 0, Diamond : 1, Heart : 2, Spade : 3, joker : 4, }; |
カードのイメージを取得するときにトランプ カードイラスト – No: 934665/無料イラストなら「イラストAC」を使うので、値は上と同じにしておかなければなりません。
Cardクラス
次にCardクラスを示します。コンストラクタの引数はスートと番号、そして親になる要素です。親になる要素とはカードはdivとして追加したいのですが、そのときに親になる要素を指します。
プロパティとしてSuit、Number、ParentElement、X、Yがあります。前三者はコンストラクタと同じです。X、Yはカードが表示される座標です。positionにabsoluteを指定して絶対配置をします。これならどこにでも配置することができます。
X、Yプロパティはとりあえず0にします。CardWidthとCardHeightはHTMLで定数として宣言されています。Imageクラスのインスタンスを生成してsrcに画像ファイルのパスを設定しています。画像が読み込まれたらcanvasを生成し、context.drawImage(image, sx, sy, sw, sh, dx, dy, dw, dh)関数をつかって範囲を指定してイメージを描画し、カードを描画するために必要なイメージを取得しています。
これを実際に試すときはサーバー上でないとエラーがでます。ローカルサーバーでもかまいません。xamppを使えば動作することを確認できます。
カードを移動させるときはParentElementに追加されているかどうかを確認し、すでに追加されている場合はいったんremoveChild関数を実行してから再度appendChild関数を実行しています。そうしないとカードが重なったときにあとからappendChild関数で追加したカードのほうが移動後も上に描画されてしまいます。最後に移動したカードを一番上に表示させたいのでこのような処理をしています。
card.js
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 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 |
class Card { constructor(suit, number, parentElement){ this.X = 0; this.Y = 0; this.Number = number; this.Suit = suit; this.ParentElement = parentElement; this.element = document.createElement('div'); this.element.style.width = CardWidth + 'px'; this.element.style.height = CardHeight + 'px'; this.element.style.position = 'absolute'; this.element.style.marginLeft = this.X + 'px'; this.element.style.marginTop = this.Y + 'px'; // カードはclass属性からカードであることがわかるようにする this.element.setAttribute('class', 'card'); const img = new Image(); img.src = "cardimage.png"; let elm = this.element; img.onload = function(){ const canvas = document.createElement('canvas'); let width = 360; let height = 500; canvas.width = width; canvas.height = height; let con = canvas.getContext('2d'); if(suit != Suit.joker) con.drawImage(img, 380 * (number - 1) + number, suit * 600, width,height, 0,0,width,height); else con.drawImage(img, 380 * (1 - 1) + 1, 4 * 600, width,height, 0,0,width,height); // カードのイメージを取得する。ここではdataURLを取得する card.dataUrl1 = canvas.toDataURL('image/png'); con.drawImage(img, 380 * (3 - 1) + 1, 4 * 600, width,height, 0,0,width,height); card.dataUrl2 = canvas.toDataURL('image/png'); // dataURLをcardImage.srcにセット let cardImage = new Image(CardWidth,CardHeight); cardImage.src = card.dataUrl1; // カードはclass属性からカードであることがわかるようにする cardImage.setAttribute('class', 'card'); elm.appendChild(cardImage); } } Move(x, y){ if(x != undefined) this.X = x; if(y != undefined) this.Y = y; for(let i=0; i< this.ParentElement.childElementCount; i++){ if(this.ParentElement.childNodes[i] == this.element){ this.ParentElement.removeChild(this.element); break; } } this.ParentElement.appendChild(this.element); this.element.style.marginLeft = this.X + 'px'; this.element.style.marginTop = this.Y + 'px'; } } |
Cardsクラス
次にCardsクラスですが、クラスのなかにCardクラスのインスタンスを格納する配列をつくっておきます。Cardクラスのインスタンスを作成したらこのなかに追加していきます。InitCards関数を使えば
カードを移動させたいときは移動対象になるものをカードのスートと番号をつかって指定したい場合と要素で指定したい場合が考えられます。クリックされたものを移動させたいのであれば要素で指定することになります。そこで配列の中からカードのスートと番号で探したいときはGetObjectFromNumber関数を、要素で探したい場合はGetObjectFromElement関数をつかって見つけることができるようにしました。
card.js
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 |
class Cards{ constructor(parentElement){ this.ParentElement = parentElement; } Cards = []; InitCards(){ this.Cards = []; for(let number=1; number<=13; number++) this.Cards.push(new Card(Suit.Spade, number, this.ParentElement)); for(let number=1; number<=13; number++) this.Cards.push(new Card(Suit.Heart, number, this.ParentElement)); for(let number=1; number<=13; number++) this.Cards.push(new Card(Suit.Diamond, number, this.ParentElement)); for(let number=1; number<=13; number++) this.Cards.push(new Card(Suit.Club, number, this.ParentElement)); // ジョーカーは2枚 this.Cards.push(new Card(Suit.joker, 1, this.ParentElement)); this.Cards.push(new Card(Suit.joker, 2, this.ParentElement)); } GetObjectFromNumber(suit, number){ for(let i=0; i< this.Cards.length; i++){ let card = this.Cards[i]; if(card.Number == number && card.Suit == suit) { return card; } } return null; } GetObjectFromElement(element){ for(let i=0; i< this.Cards.length; i++){ let card = this.Cards[i]; if(card.element == element) { return card; } if(card.element.children[0] == element) { return card; } } return null; } } |
あとはCardsクラスのインスタンスを生成してInitCards関数を実行したあとShowAllCards関数を実行すればすべてのカードを表示させることができます。