JavaScriptで描くトランプの図柄

## トランプの図柄

[前回の記事](https://qiita.com/v416/items/94c61b7dc712b7c16ddc)では Canvas API の基本的な扱いや注意点を述べました。今回はそのことを踏まえ、トランプの画像を Canvas API で描いていこうと思います。

それでは改めてトランプの図柄を見ていきましょう。縦横ともに 48px の正方形でカード部分は白で塗りつぶし、黒で囲います。その上に、上部にスート（記号）、下部にランク（数値）を配置する。これが基本形となります。
![spade01.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/639913/d9f9b885-70ef-8d02-a7ef-a83a41cb2ce7.png)

今回も雛形となる html ファイルを用意しましょう。

    <!DOCTYPE html>
    <html lang="ja">
      <head>
        <meta charset="utf-8"/>
      </head>
      <body>
        <canvas id="cvs">
        <script>
        </script>
      </body>
    </html>

それではコードを書いていきましょう。

## ベースの描画

今回は canvas 要素取得した段階で、縦横のサイズを設定しましょう。

    const canvas = document.getElementById('cvs');
    canvas.width = 48;
    canvas.height = 48;
    const ctx = canvas.getContext('2d');

canvas の基本サイズは縦 150px の横 300px です。きちんとサイズを変更してやらないと余計な空白ができてしまいます。あと CSS で指定するサイズとは別物ですので注意して下さい。

それではこの canvas に、単純な四角形を描いてみましょう。

    ctx.beginPath();
    ctx.moveTo( 0, 0);
    ctx.lineTo(47, 0);
    ctx.lineTo(47, 47);
    ctx.lineTo(0, 47);
    ctx.closePath();
    ctx.stroke();

上と左の線が細いですね。これは指定した座標を中心として線が描画されるため生じる現象です。つまり左は -0.5 から 0.5 の 1px 分、右は 46.5 から 47.5 の 1px 分の太さで線を描画している、結果、左側は 0.5px 分の細い線になってしまうというわけです。なので 0 となっているところはすべて 1 に変更しましょう。

それではこの四角形の角を落としてみましょう。

    ctx.beginPath();
    ctx.moveTo( 6, 1);
    ctx.lineTo(42, 1);
    ctx.lineTo(47, 6);
    ctx.lineTo(47, 42);
    ctx.lineTo(42, 47);
    ctx.lineTo(6, 47);
    ctx.lineTo(1, 42);
    ctx.lineTo(1, 6);
    ctx.closePath();
    ctx.stroke();

落とされた角は、まだ直線で繋げられています。この角を、弧を使ってつなげてみましょう。

まず右下の角に注目しましょう。この角は三時から六時の 90 度で弧を描く必要があります。前回も述べた通り arc の描画開始角度は三時を 0 度とします。なので以下のように書けば求める描画ができます。

    ctx.beginPath();
    ctx.moveTo( 6, 1);
    ctx.lineTo(42, 1);
    ctx.lineTo(47, 6);
    ctx.lineTo(47, 42);
    // ctx.lineTo(42, 47);
    ctx.arc(42, 42, 5, 0, Math.PI / 2);
    ctx.lineTo(6, 47);
    ctx.lineTo(1, 42);
    ctx.lineTo(1, 6);
    ctx.closePath();
    ctx.stroke();

それでは左下はどうコードを書けばいいでしょうか？　六時を開始角度として 90 度の弧を描けばいいのだから、以下のようなコードを思い浮かべると思います。

    ctx.arc(6, 42, 5, Math.PI / 2, Math.PI / 2);

しかしこのコードでは思い通りの描画がされません。arc の終点角度は開始角度から何度というものではなく、円全体のどの角度で終わるかを指定する必要があります。終点角度は九時の角度になりますから丁度 180 度、ラジアンだと Math.PI となります。よってコードは以下の通りとなります。

    ctx.beginPath();
    ctx.moveTo( 6, 1);
    ctx.lineTo(42, 1);
    ctx.lineTo(47, 6);
    ctx.lineTo(47, 42);
    // ctx.lineTo(42, 47);
    ctx.arc(42, 42, 5, 0, Math.PI / 2);
    ctx.lineTo(6, 47);
    // ctx.lineTo(1, 42);
    ctx.arc(6, 42, 5, Math.PI / 2, Math.PI);
    ctx.lineTo(1, 6);
    ctx.closePath();
    ctx.stroke();

それでは上のふたつの角も丸めてみましょう。

    ctx.beginPath();
    ctx.moveTo( 6, 1);
    ctx.lineTo(42, 1);
    // ctx.lineTo(47, 6);
    ctx.arc(42, 6, 5, -1 * Math.PI / 2, 0);
    ctx.lineTo(47, 42);
    // ctx.lineTo(42, 47);
    ctx.arc(42, 42, 5, 0, Math.PI / 2);
    ctx.lineTo(6, 47);
    // ctx.lineTo(1, 42);
    ctx.arc(6, 42, 5, Math.PI / 2, Math.PI);
    ctx.lineTo(1, 6);
    ctx.arc(6, 6, 5, Math.PI, -1 * Math.PI / 2);
    ctx.closePath();
    ctx.stroke();

角は中心座標と半径を調節して、好みの弧を描いてみて下さい。

さて、枠が描けたところで body 要素に背景色を設定してみてくださ。

    <body style="background-color:forestgreen">

枠の中も緑色になってしまいますね。そう、塗りつぶしをまだ行っていませんでした。

fill も stroke も、デフォルトの色は黒になります。塗りつぶしは白で行うので、まずは色を変更しましょう。

    ctx.fillStyle = "white";

そして塗りつぶしですが、線描画を行う前に塗りつぶしを行って下さい。順番を逆にしてしまうと線も塗りつぶされてしまいます。

    ctx.fill();
    ctx.stroke();

## スートとランク

文字列を書き込むには fillText というメソッドを使います。

    ctx.fillText("A", 24, 34);

ポイントとなるのは座標です。メソッドで指定する座標は、デフォルトでは文字列の左下を示すものになります。ですがこの状態では扱いづらいので、文字列の縦横中央を示すよう設定を変更します。

    ctx.fillStyle = "black";
    ctx.textAlign = "center";
    ctx.textBaseline = "middle";

fillText という名称から分かる通り、塗りつぶしを行うのでまず色を黒に変更して下さい。それからtextAlign で文字列の横軸を center に設定します。そして textBaseline で文字列の縦軸を middle に設定します。

指定する座標は縦横 48px なので横軸は 24 に、縦軸は上が 12 で下が 36 になります。ですがフォントの種類や座標によって変わってくるところなので、ご自身で納得の位置を探してみて下さい。今回は以下のようにしました。

    ctx.font = "24px 'sans-serif'";
    ctx.fillText("♠", 24, 14);
    ctx.font = "18px 'sans-serif'";
    ctx.fillText("A", 24, 34);

font プロパティはフォントサイズとフォント名の組み合わせが必須なので気をつけて下さい。

すこし長くなってしまったので、今回は最後にここまでのコードをアップして次回につなげようと思います。

それでは。

    const canvas = document.getElementById('cvs');
    canvas.width = 48;
    canvas.height = 48;
    const ctx = canvas.getContext('2d');
    
    ctx.fillStyle = "white";
    ctx.strokeStyle = "black";
    
    ctx.beginPath();
    ctx.moveTo( 6, 1);
    ctx.lineTo(42, 1);
    ctx.arc(42, 6, 5, -1 * Math.PI / 2, 0);
    ctx.lineTo(47, 42);
    ctx.arc(42, 42, 5, 0, Math.PI / 2);
    ctx.lineTo(6, 47);
    ctx.arc(6, 42, 5, Math.PI / 2, Math.PI);
    ctx.lineTo(1, 6);
    ctx.arc(6, 6, 5, Math.PI, -1 * Math.PI / 2);
    ctx.closePath();
    ctx.fill();
    ctx.stroke();

    ctx.fillStyle = "black";
    ctx.textAlign = "center";
    ctx.textBaseline = "middle";

    ctx.font = "24px 'sans-serif'";
    ctx.fillText("♠", 24, 14);
    ctx.font = "18px 'sans-serif'";
    ctx.fillText("A", 24, 34);